From c929075fab7020722ab5e57aae77d9a172503abb Mon Sep 17 00:00:00 2001 From: Akamaru Date: Sun, 12 Oct 2025 15:52:11 +0200 Subject: [PATCH] Neu: Country Flag Fixer --- country-flag-fixer.user.js | 219 +++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 country-flag-fixer.user.js diff --git a/country-flag-fixer.user.js b/country-flag-fixer.user.js new file mode 100644 index 0000000..9ddcc91 --- /dev/null +++ b/country-flag-fixer.user.js @@ -0,0 +1,219 @@ +// ==UserScript== +// @name Country Flag Fixer +// @namespace https://git.ponywave.de/Akamaru/Userscripts +// @version 1.0 +// @description Ersetzt Ländercodes mit echten Flaggen-Emojis unter Windows/Chromium +// @author Akamaru +// @match *://*/* +// @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== + +// Source: https://github.com/talkjs/country-flag-emoji-polyfill +(function() { + 'use strict'; + + const replacementFontName = "Twemoji Country Flags"; + const extentionStyleTagId = "country-flag-fixer-ext"; + const headStyleTagId = "country-flag-fixer-ext-head"; + + /** + * Registriere die Custom Font-Face für Flaggen-Emojis + */ + const loadFontFace = () => { + const fontUrl = GM_getResourceURL('TwemojiCountryFlags'); + const style = document.createElement("style"); + style.setAttribute("type", "text/css"); + style.setAttribute("id", headStyleTagId); + + // Unicode range generiert von: https://wakamaifondue.com/beta/ + 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; /* Verhindert kursive Flaggen */ + 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; + } + `; + + // Prüfe auf Fälle wie SVG-Dateien ohne head-Element + if (document.head != undefined) { + document.head.appendChild(style); + } + }; + + /** + * Extrahiere font-family Regeln aus allen Stylesheets + */ + const extractFontFamilyRules = () => { + const fontFamilyRules = []; + + for (const sheet of document.styleSheets) { + + // Ignoriere Styles von dieser Extension + if (sheet.ownerNode?.id == extentionStyleTagId || sheet.ownerNode?.id == headStyleTagId) + continue; + + // Ignoriere non-screen Stylesheets + const sheetMediaBlacklist = ['print', 'speech', 'aural', 'braille', 'handheld', 'projection', 'tty']; + if (sheetMediaBlacklist.includes(sheet.media.mediaText)) + continue; + + try { + // Loop durch jeden CSS-Selektor im Stylesheet + for (const rule of sheet.cssRules) { + + if (!rule.style || !rule.style?.fontFamily) + continue; + + const selectorText = rule.selectorText; + const fontFamily = rule.style.fontFamily; + + // 'inherit' kann nicht mit anderen Fonts kombiniert werden + if (fontFamily == 'inherit') + continue; + + // Bereits modifizierte Selektoren ignorieren + if (fontFamily.toLowerCase().includes(replacementFontName.toLowerCase())) + continue; + + fontFamilyRules.push({ selectorText, fontFamily }); + } + } + catch (e) { + // Manche Stylesheets sind wegen CORS nicht zugreifbar + } + } + + return fontFamilyRules; + }; + + /** + * Erstelle neues Style-Tag mit überschriebenen font-family Regeln + */ + const createNewStyleTag = (fontFamilyRules) => { + const style = document.createElement("style"); + style.setAttribute("type", "text/css"); + style.setAttribute("id", extentionStyleTagId); + + fontFamilyRules.forEach((rule) => { + // Setze Country Flags Font als Hauptfont; Original als Fallback + style.textContent += `${rule.selectorText} { font-family: '${replacementFontName}', ${rule.fontFamily} !important; }\n`; + }); + + return style; + }; + + /** + * Wende Custom Font Styles auf die Seite an + */ + const applyCustomFontStyles = () => { + const existingSheet = document.getElementById(extentionStyleTagId); + + const fontFamilyRules = extractFontFamilyRules(); + const newStyleTag = createNewStyleTag(fontFamilyRules); + + // Schreibe überschriebene Styles komplett neu + if (existingSheet) { + existingSheet.parentNode.removeChild(existingSheet); + } + + if (document.head == null) + return; + + document.head.appendChild(newStyleTag); + }; + + /** + * Bewahre Custom Fonts in inline styles + */ + const preserveCustomFonts = (element) => { + if (element == undefined) + return; + + // Ignoriere Elemente ohne style-Attribut oder font-family + const inlineStyle = element.getAttribute('style'); + if (!inlineStyle || !inlineStyle.includes('font-family')) + return; + + // Font family regex für Font (Gruppe 1) und !important Modifier (Gruppe 2) + 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'); + }; + + /** + * Initialisiere das Script + */ + const init = () => { + // Lade Font-Face + loadFontFace(); + + // Warte bis DOM bereit ist + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + applyCustomFontStyles(); + }); + } else { + applyCustomFontStyles(); + } + + // Beobachte Dokument auf dynamisch hinzugefügte Styles + let lastStyleSheets = new Set(Array.from(document.styleSheets).map(sheet => sheet.href || sheet.ownerNode?.textContent)); + const observer = new MutationObserver((mutations) => { + let stylesheetChanged = false; + + mutations.forEach(mutation => { + // Fokussiere nur auf und