Adds some features on github.com & gitlab.com to integrate it with tecnativa.com
< 腳本TGithub的回應
Can you review and update with this code?
// ==UserScript== // @name TGithub // @author Alexandre Díaz, Carlos Roca, Carlos Dauden // @version 1.18.0 // @grant none // @run-at document-idle // @namespace tecnativa // @icon https://www.tecnativa.com/web/image/website/1/favicon/ // @match *://github.com/* // @match *://gitlab.tecnativa.com/* // @description Adds some features on github.com & gitlab.com to integrate it with tecnativa.com // @downloadURL https://update.greasyfork.org/scripts/469159/TGithub.user.js // @updateURL https://update.greasyfork.org/scripts/469159/TGithub.meta.js // ==/UserScript== (function (window) { "use strict"; const TGithub = { ODOO_SERVER: 'https://www.tecnativa.com', COMPANY_NAME: 'Tecnativa', REGEX_TEMPLATES: {}, initialized: false, observer: null, navbarObserver: null, init() { if (this.initialized) return; this.initialized = true; this._addRegexTemplate( 'TT', /\bTT(\d+)/gi, `<a target="_blank" href="${this.ODOO_SERVER}/web#id=$1&model=project.task&view_type=form">${this.COMPANY_NAME}-Task #$1</a>` ); this._replaceTask(); if (this._isLocationHost('github')) { this._ensureGhNavbarButton(); this._observeNavbar(); } }, /* ================= CORE ================= */ _isLocationHost(host) { return document.location.host.toLowerCase().includes(host); }, _addRegexTemplate(name, regex, html) { this.REGEX_TEMPLATES[name] = { regex, html }; }, _executeRegexReplace(templateName, text) { const tpl = this.REGEX_TEMPLATES[templateName]; if (!tpl) return false; const result = text.replace(tpl.regex, tpl.html); return result !== text ? result : false; }, /* ================= TASK REPLACEMENT ================= */ _replaceTask() { const processNode = (elm) => { if (elm.dataset.tgithubProcessed) return; const newHTML = this._executeRegexReplace('TT', elm.innerHTML); if (newHTML) { elm.innerHTML = newHTML; } elm.dataset.tgithubProcessed = "1"; }; const scan = () => { document.querySelectorAll( '.comment-body, .note-text, .description, .commit-description, .js-comment-body, .js-issue-title, .markdown-body' ).forEach(processNode); }; // Optimized observer with RAF debounce if (!this.observer) { let scheduled = false; this.observer = new MutationObserver(() => { if (scheduled) return; scheduled = true; requestAnimationFrame(() => { scan(); scheduled = false; }); }); this.observer.observe(document.body, { childList: true, subtree: true }); } // Initial scan scan(); }, /* ================= GITHUB NAVBAR ================= */ _findGhHeaderTarget() { return ( document.querySelector('.AppHeader-globalBar-end') || document.querySelector('div[data-testid="top-bar-actions"]') ); }, _ensureGhNavbarButton() { const target = this._findGhHeaderTarget(); if (!target) return false; if (target.querySelector('#pulls-tecnativa')) return true; const menuItem = document.createElement('a'); menuItem.id = 'pulls-tecnativa'; menuItem.textContent = this.COMPANY_NAME; menuItem.href = `/pulls?q=is%3Aopen+is%3Apr+archived%3Afalse+involves%3A${this.COMPANY_NAME}`; const ref = target.querySelector("a[href$='/pulls']") || target.querySelector("a[href*='/pulls']") || target.querySelector('a'); if (ref) { menuItem.className = ref.className; menuItem.style.cssText = (ref.style.cssText || '') + 'width:auto;'; } else { menuItem.style.cssText = 'margin-right:8px; display:inline-flex; align-items:center;'; } target.insertAdjacentElement('afterbegin', menuItem); return true; }, _observeNavbar() { if (this.navbarObserver) return; this.navbarObserver = new MutationObserver(() => { const target = this._findGhHeaderTarget(); if (!target) return; if (!target.querySelector('#pulls-tecnativa')) { this._ensureGhNavbarButton(); } }); this.navbarObserver.observe(document.body, { childList: true, subtree: true }); }, /* ================= SPA HANDLING ================= */ attachNavigationEvents() { // GitHub Turbo navigation (clave) document.addEventListener('turbo:load', () => { this.initialized = false; this.init(); }); // Fallback genérico window.addEventListener('popstate', () => { this.initialized = false; this.init(); }); } }; /* ================= BOOT ================= */ const ready = (cb) => { if (document.readyState !== 'loading') { cb(); } else { document.addEventListener('DOMContentLoaded', cb); } }; ready(() => { TGithub.init(); TGithub.attachNavigationEvents(); }); })(window);
登入以回覆
Can you review and update with this code?