Skip to content

Instantly share code, notes, and snippets.

@ImoutoHeaven
Created June 14, 2026 14:45
Show Gist options
  • Select an option

  • Save ImoutoHeaven/e23fc7c73c46f944acfbb10d29e7d426 to your computer and use it in GitHub Desktop.

Select an option

Save ImoutoHeaven/e23fc7c73c46f944acfbb10d29e7d426 to your computer and use it in GitHub Desktop.
mojibake-fixer
// ==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