// ==UserScript== // @name Country Flag Fixer // @namespace https://git.ponywave.de/Akamaru/Userscripts // @version 1.2 // @description Ersetzt Ländercodes mit echten Flaggen-Emojis unter Windows/Chromium // @author Akamaru // @match *://*/* // @icon https://www.google.com/s2/favicons?domain=git.ponywave.de&sz=32 // @grant GM_getResourceURL // @resource TwemojiCountryFlags https://cdn.jsdelivr.net/npm/country-flag-emoji-polyfill@0.1.8/dist/TwemojiCountryFlags.woff2 // @updateURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/country-flag-fixer.user.js // @downloadURL https://git.ponywave.de/Akamaru/Userscripts/raw/branch/master/country-flag-fixer.user.js // @run-at document-start // ==/UserScript== (function() { 'use strict'; const replacementFontName = "Twemoji Country Flags"; const extentionStyleTagId = "country-flag-fixer-ext"; const headStyleTagId = "country-flag-fixer-ext-head"; const loadFontFace = () => { const fontUrl = GM_getResourceURL('TwemojiCountryFlags'); const style = document.createElement("style"); style.setAttribute("type", "text/css"); style.setAttribute("id", headStyleTagId); style.textContent = ` @font-face { font-family: "${replacementFontName}"; font-style: normal; src: url('${fontUrl}') format('woff2'); unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F; } @font-face { font-family: "${replacementFontName}"; font-style: italic; src: url('${fontUrl}') format('woff2'); unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F; } `; if (document.head != undefined) { document.head.appendChild(style); } }; const extractFontFamilyRules = () => { const fontFamilyRules = []; for (const sheet of document.styleSheets) { if (sheet.ownerNode?.id == extentionStyleTagId || sheet.ownerNode?.id == headStyleTagId) continue; const sheetMediaBlacklist = ['print', 'speech', 'aural', 'braille', 'handheld', 'projection', 'tty']; if (sheetMediaBlacklist.includes(sheet.media.mediaText)) continue; try { for (const rule of sheet.cssRules) { if (!rule.style || !rule.style?.fontFamily) continue; const selectorText = rule.selectorText; const fontFamily = rule.style.fontFamily; if (fontFamily == 'inherit') continue; if (fontFamily.toLowerCase().includes(replacementFontName.toLowerCase())) continue; fontFamilyRules.push({ selectorText, fontFamily }); } } catch (e) { } } return fontFamilyRules; }; const createNewStyleTag = (fontFamilyRules) => { const style = document.createElement("style"); style.setAttribute("type", "text/css"); style.setAttribute("id", extentionStyleTagId); fontFamilyRules.forEach((rule) => { style.textContent += `${rule.selectorText} { font-family: '${replacementFontName}', ${rule.fontFamily} !important; }\n`; }); return style; }; const applyCustomFontStyles = () => { const existingSheet = document.getElementById(extentionStyleTagId); const fontFamilyRules = extractFontFamilyRules(); const newStyleTag = createNewStyleTag(fontFamilyRules); if (existingSheet) { existingSheet.parentNode.removeChild(existingSheet); } if (document.head == null) return; document.head.appendChild(newStyleTag); }; const preserveCustomFonts = (element) => { if (element == undefined) return; const inlineStyle = element.getAttribute('style'); if (!inlineStyle || !inlineStyle.includes('font-family')) return; const fontFamilyRegex = /font-family\s*:\s*([^;]+?)(\s*!important)?\s*(;|$)/; const match = fontFamilyRegex.exec(inlineStyle); if (!match) return; const hasIsImportant = match[2] && match[2].includes('!important'); if (hasIsImportant) return; const currentFontFamily = match[1].trim(); element.style.setProperty('font-family', currentFontFamily, 'important'); }; const processInitialElements = () => { document.querySelectorAll('[style*="font-family"]').forEach(preserveCustomFonts); }; const init = () => { loadFontFace(); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { applyCustomFontStyles(); processInitialElements(); }); } else { applyCustomFontStyles(); processInitialElements(); } let lastStyleSheets = new Set(Array.from(document.styleSheets).map(sheet => sheet.href || sheet.ownerNode?.textContent)); const observer = new MutationObserver((mutations) => { let stylesheetChanged = false; const elementsToProcess = new Set(); mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.id === extentionStyleTagId || node.id === headStyleTagId) return; const isStylesheet = node.nodeName === 'LINK' && node.rel === 'stylesheet'; const isStyleNode = node.nodeName === 'STYLE'; if (isStylesheet || isStyleNode) { const newStylesheetIdentifier = isStylesheet ? node.href : node.textContent; if (!lastStyleSheets.has(newStylesheetIdentifier)) { stylesheetChanged = true; lastStyleSheets.add(newStylesheetIdentifier); } return; } if (node.nodeType === Node.ELEMENT_NODE) { if (node.hasAttribute?.('style') && node.getAttribute('style').includes('font-family')) { elementsToProcess.add(node); } node.querySelectorAll?.('[style*="font-family"]').forEach(el => elementsToProcess.add(el)); } }); if (mutation.type === 'attributes' && mutation.attributeName === 'style') { const target = mutation.target; if (target.hasAttribute('style') && target.getAttribute('style').includes('font-family')) { elementsToProcess.add(target); } } }); if (stylesheetChanged) { applyCustomFontStyles(); } elementsToProcess.forEach(preserveCustomFonts); }); observer.observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: ['style'] }); }; init(); })();