Created
June 14, 2026 14:45
-
-
Save ImoutoHeaven/e23fc7c73c46f944acfbb10d29e7d426 to your computer and use it in GitHub Desktop.
mojibake-fixer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Osamurai Mojibake Fix | |
| // @namespace https://osamurai.azimech.net/ | |
| // @version 1.0.0 | |
| // @description Fix UTF-8 mojibake on osamurai.azimech.net pages. | |
| // @match https://osamurai.azimech.net/* | |
| // @run-at document-end | |
| // @grant none | |
| // ==/UserScript== | |
| (() => { | |
| 'use strict'; | |
| const fixableAttributes = new Set([ | |
| 'alt', | |
| 'aria-label', | |
| 'content', | |
| 'placeholder', | |
| 'title', | |
| ]); | |
| const fixMojibake = value => { | |
| if (typeof value !== 'string' || value === '') { | |
| return value; | |
| } | |
| try { | |
| const bytes = Uint8Array.from(value, character => { | |
| const code = character.charCodeAt(0); | |
| if (code > 255) { | |
| throw new RangeError('not single-byte mojibake'); | |
| } | |
| return code; | |
| }); | |
| return new TextDecoder('utf-8', { fatal: true }).decode(bytes); | |
| } catch { | |
| return value; | |
| } | |
| }; | |
| const fixTextNodes = root => { | |
| const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); | |
| let node; | |
| while ((node = walker.nextNode())) { | |
| node.nodeValue = fixMojibake(node.nodeValue); | |
| } | |
| }; | |
| const fixAttributes = root => { | |
| for (const element of root.querySelectorAll('*')) { | |
| for (const attribute of element.attributes) { | |
| if (fixableAttributes.has(attribute.name)) { | |
| element.setAttribute(attribute.name, fixMojibake(attribute.value)); | |
| } | |
| } | |
| } | |
| }; | |
| const fixRoot = root => { | |
| fixTextNodes(root); | |
| fixAttributes(root); | |
| }; | |
| fixRoot(document.body); | |
| new MutationObserver(mutations => { | |
| for (const mutation of mutations) { | |
| for (const node of mutation.addedNodes) { | |
| if (node.nodeType === Node.TEXT_NODE) { | |
| node.nodeValue = fixMojibake(node.nodeValue); | |
| } else if (node.nodeType === Node.ELEMENT_NODE) { | |
| fixRoot(node); | |
| } | |
| } | |
| if (mutation.type === 'attributes' && fixableAttributes.has(mutation.attributeName)) { | |
| const value = mutation.target.getAttribute(mutation.attributeName); | |
| mutation.target.setAttribute(mutation.attributeName, fixMojibake(value)); | |
| } | |
| } | |
| }).observe(document.body, { | |
| attributeFilter: [...fixableAttributes], | |
| attributes: true, | |
| childList: true, | |
| subtree: true, | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment