Greasy Fork

Greasy Fork is available in English.

Tâi-gí「教典」TL ⇄ POJ

予代管當局 ROC 教育部 Tâi-gí 常用詞詞典網站呈現出 POJ!(對臺羅換過來、換轉去)

当前为 2025-03-27 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Tâi-gí「教典」TL ⇄ POJ
// @namespace    aiuanyu
// @version      2.0b
// @description  予代管當局 ROC 教育部 Tâi-gí 常用詞詞典網站呈現出 POJ!(對臺羅換過來、換轉去)
// @author       Aiuanyu 愛灣語, TongcyDai
// @match        http*://sutian.moe.edu.tw/*
// @grant        none
// @license      GNU GPLv3
// ==/UserScript==

(function() {
    'use strict';

    // 預設使用 POJ
    let usePOJ = localStorage.getItem('sutianUsePOJ') !== 'false';

    // 囥原始文字的 mapping table
    const originalTextMap = new WeakMap();

    // 切換揤鈕
    function createToggleButton() {
        const button = document.createElement('button');
        button.id = 'poj-toggle-btn';
        button.textContent = usePOJ ? '用臺羅 (TL)' : '用白話字 (POJ)';
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.left = '20px';
        button.style.zIndex = '9999';
        button.style.padding = '8px 12px';
        button.style.backgroundColor = '#3498db';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '4px';
        button.style.cursor = 'pointer';
        button.style.fontWeight = 'bold';
        button.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';

        // 鼠仔滑過的效果
        button.addEventListener('mouseover', function() {
            this.style.backgroundColor = '#2980b9';
        });

        button.addEventListener('mouseout', function() {
            this.style.backgroundColor = '#3498db';
        });

        // 「揤」事件:切換羅馬字
        button.addEventListener('click', toggleRomanization);

        document.body.appendChild(button);
    }

    // 切換羅馬字
    function toggleRomanization() {
        usePOJ = !usePOJ;
        localStorage.setItem('sutianUsePOJ', usePOJ);

        const button = document.getElementById('poj-toggle-btn');
        if (button) {
            button.textContent = usePOJ ? '用臺羅 (TL)' : '用白話字 (POJ)';
        }

        if (usePOJ) {
            applyPOJTransformation(document.body);
        } else {
            restoreOriginalText(document.body);
        }
    }

    // 共文字對臺羅轉做白話字
    function applyPOJTransformation(node) {
        if (node.nodeType === Node.TEXT_NODE) {
            // 收囥原始文字
            if (!originalTextMap.has(node)) {
                originalTextMap.set(node, node.nodeValue);
            }

            let text = node.nodeValue;
            text = text.normalize('NFD').normalize('NFC');

            // ts → ch
            text = text.replace(/(?<=^|\W)ts/gi, function(match) { return (match[0] === 'T' ? 'Ch' : 'ch'); });

            // ua → oa
            // 處理 ua 後面接 i、n、t、h 的情形 (調符徙去 a 頂懸)
            text = text.replace(/u([aáàâǎāa̍a̋])([inth](?:\W|$))/gi, function(match, vowel, end) { return (match[0] === 'U' ? 'O' : 'o') + vowel + end; });

            // 處理孤一个 ua 抑是後壁接 nn 的情形 (調符徙去 o 頂懸,nn 上尾才換)
            text = text.replace(/ua(?=nn\W|nn$|\W|$)/gi, function(match) { return (match[0] === 'U' ? 'O' : 'o') + 'a'; });
            text = text.replace(/uá(?=nn\W|nn$|\W|$)/gi, function(match) { return (match[0] === 'U' ? 'Ó' : 'ó') + 'a'; });
            text = text.replace(/uà(?=nn\W|nn$|\W|$)/gi, function(match) { return (match[0] === 'U' ? 'Ò' : 'ò') + 'a'; });
            text = text.replace(/uâ(?=nn\W|nn$|\W|$)/gi, function(match) { return (match[0] === 'U' ? 'Ô' : 'ô') + 'a'; });
            text = text.replace(/uǎ(?=nn\W|nn$|\W|$)/gi, function(match) { return (match[0] === 'U' ? 'Õ' : 'õ') + 'a'; });
            text = text.replace(/uā(?=nn\W|nn$|\W|$)/gi, function(match) { return (match[0] === 'U' ? 'Ō' : 'ō') + 'a'; });
            text = text.replace(/ua̍(?=nn\W|nn$|\W|$)/gi, function(match) { return (match[0] === 'U' ? 'O̍' : 'o̍') + 'a'; });
            text = text.replace(/ua̋(?=nn\W|nn$|\W|$)/gi, function(match) { return (match[0] === 'U' ? 'Ŏ' : 'ŏ') + 'a'; });

            // ue → oe (除了 ueh 以外,調符攏徙去 o 頂懸)
            text = text.replace(/ue/gi, function(match) { return (match[0] === 'U' ? 'O' : 'o') + 'e'; });
            text = text.replace(/ué/gi, function(match) { return (match[0] === 'U' ? 'Ó' : 'ó') + 'e'; });
            text = text.replace(/uè/gi, function(match) { return (match[0] === 'U' ? 'Ò' : 'ò') + 'e'; });
            text = text.replace(/uê/gi, function(match) { return (match[0] === 'U' ? 'Ô' : 'ô') + 'e'; });
            text = text.replace(/uě/gi, function(match) { return (match[0] === 'U' ? 'Õ' : 'õ') + 'e'; });
            text = text.replace(/uē/gi, function(match) { return (match[0] === 'U' ? 'Ō' : 'ō') + 'e'; });
            text = text.replace(/ue̍/gi, function(match) { return (match[0] === 'U' ? 'Oe̍' : 'oe̍'); });
            text = text.replace(/ue̋/gi, function(match) { return (match[0] === 'U' ? 'Ŏ' : 'ŏ') + 'e'; });

            // ui → ui (調符徙去 u 頂懸)
            // text = text.replace(/ui/gi, function(match) { return (match[0] === 'U' ? 'U' : 'u') + 'i'; });
            text = text.replace(/uí/gi, function(match) { return (match[0] === 'U' ? 'Ú' : 'ú') + 'i'; });
            text = text.replace(/uì/gi, function(match) { return (match[0] === 'U' ? 'Ù' : 'ù') + 'i'; });
            text = text.replace(/uî/gi, function(match) { return (match[0] === 'U' ? 'Û' : 'û') + 'i'; });
            text = text.replace(/uǐ/gi, function(match) { return (match[0] === 'U' ? 'Ũ' : 'ũ') + 'i'; });
            text = text.replace(/uī/gi, function(match) { return (match[0] === 'U' ? 'Ū' : 'ū') + 'i'; });
            text = text.replace(/ui̍/gi, function(match) { return (match[0] === 'U' ? 'U̍' : 'u̍') + 'i'; });
            text = text.replace(/ui̋/gi, function(match) { return (match[0] === 'U' ? 'Ŭ' : 'ŭ') + 'i'; });

            // oo → o͘
            text = text.replace(/oo(?=\W|$)/gi, function(match) { return (match[0] === 'O' ? 'O͘' : 'o͘'); });
            text = text.replace(/óo(?=\W|$)/gi, function(match) { return (match[0] === 'Ó' ? 'Ó͘' : 'ó͘'); });
            text = text.replace(/òo(?=\W|$)/gi, function(match) { return (match[0] === 'Ò' ? 'Ò͘' : 'ò͘'); });
            text = text.replace(/ôo(?=\W|$)/gi, function(match) { return (match[0] === 'Ô' ? 'Ô͘' : 'ô͘'); });
            text = text.replace(/ǒo(?=\W|$)/gi, function(match) { return (match[0] === 'Ǒ' ? 'Õ͘' : 'õ͘'); });
            text = text.replace(/ōo(?=\W|$)/gi, function(match) { return (match[0] === 'Ō' ? 'Ō͘' : 'ō͘'); });
            text = text.replace(/ooh(?=\W|$)/gi, function(match) { return (match[0] === 'O' ? 'O͘h' : 'o͘h'); }); // 替代處理,才袂換過頭
            text = text.replace(/o̍oh(?=\W|$)/gi, function(match) { return (match[0] === 'O' ? 'O̍͘h' : 'o̍͘h'); });
            text = text.replace(/őo(?=\W|$)/gi, function(match) { return (match[0] === 'Ő' ? 'Ŏ͘' : 'ŏ͘'); });

            // ee → e͘
            text = text.replace(/ee/gi, function(match) { return (match[0] === 'E' ? 'E͘' : 'e͘'); });
            text = text.replace(/ée/gi, function(match) { return (match[0] === 'É' ? 'É͘' : 'é͘'); });
            text = text.replace(/èe/gi, function(match) { return (match[0] === 'È' ? 'È͘' : 'è͘'); });
            text = text.replace(/êe/gi, function(match) { return (match[0] === 'Ê' ? 'Ê͘' : 'ê͘'); });
            text = text.replace(/ěe/gi, function(match) { return (match[0] === 'Ě' ? 'Ẽ͘' : 'ẽ͘'); });
            text = text.replace(/ēe/gi, function(match) { return (match[0] === 'Ē' ? 'Ē͘' : 'ē͘'); });
            text = text.replace(/e̍e/gi, function(match) { return (match[0] === 'E' ? 'E̍͘' : 'e̍͘'); });
            text = text.replace(/e̋e/gi, function(match) { return (match[0] === 'E' ? 'Ĕ͘' : 'ĕ͘'); });

            // or → o (白話字無分 o、or)
            text = text.replace(/or/gi, function(match) { return (match[0] === 'O' ? 'O' : 'o'); });
            text = text.replace(/or/gi, function(match) { return (match[0] === 'Ó' ? 'Ó' : 'ó'); });
            text = text.replace(/òr/gi, function(match) { return (match[0] === 'Ò' ? 'Ò' : 'ò'); });
            text = text.replace(/ôr/gi, function(match) { return (match[0] === 'Ô' ? 'Ô' : 'ô'); });
            text = text.replace(/ǒr/gi, function(match) { return (match[0] === 'Ǒ' ? 'Õ' : 'õ'); });
            text = text.replace(/ōr/gi, function(match) { return (match[0] === 'Ō' ? 'Ō' : 'ō'); });
            text = text.replace(/o̍r/gi, function(match) { return (match[0] === 'O' ? 'O̍' : 'o̍'); });
            text = text.replace(/őr/gi, function(match) { return (match[0] === 'Ő' ? 'Ŏ' : 'ŏ'); });

            // er → o̤
            text = text.replace(/er/gi, function(match) { return (match[0] === 'E' ? 'O̤' : 'o̤'); });
            text = text.replace(/ér/gi, function(match) { return (match[0] === 'É' ? 'Ó̤' : 'ó̤'); });
            text = text.replace(/èr/gi, function(match) { return (match[0] === 'È' ? 'Ò̤' : 'ò̤'); });
            text = text.replace(/êr/gi, function(match) { return (match[0] === 'Ê' ? 'Ô̤' : 'ô̤'); });
            text = text.replace(/ěr/gi, function(match) { return (match[0] === 'Ě' ? 'Õ̤' : 'õ̤'); });
            text = text.replace(/ēr/gi, function(match) { return (match[0] === 'Ē' ? 'Ō̤' : 'ō̤'); });
            text = text.replace(/e̍r/gi, function(match) { return (match[0] === 'E' ? 'O̤̍' : 'o̤̍'); });
            text = text.replace(/e̋r/gi, function(match) { return (match[0] === 'E' ? 'Ŏ̤' : 'ŏ̤'); });

            // ir → ṳ
            text = text.replace(/ir/gi, function(match) { return (match[0] === 'I' ? 'Ṳ' : 'ṳ'); });
            text = text.replace(/ír/gi, function(match) { return (match[0] === 'Í' ? 'Ṳ́' : 'ṳ́'); });
            text = text.replace(/ìr/gi, function(match) { return (match[0] === 'Ì' ? 'Ṳ̀' : 'ṳ̀'); });
            text = text.replace(/îr/gi, function(match) { return (match[0] === 'Î' ? 'Ṳ̂' : 'ṳ̂'); });
            text = text.replace(/ǐr/gi, function(match) { return (match[0] === 'Ǐ' ? 'Ṳ̃' : 'ṳ̃'); });
            text = text.replace(/īr/gi, function(match) { return (match[0] === 'Ī' ? 'Ṳ̄' : 'ṳ̄'); });
            text = text.replace(/i̍r/gi, function(match) { return (match[0] === 'I' ? 'Ṳ̍' : 'ṳ̍'); });
            text = text.replace(/i̋r/gi, function(match) { return (match[0] === 'I' ? 'Ṳ̆' : 'ṳ̆'); });

            // nn → ⁿ (干焦 nn 佇音節尾的時才會換)
            text = text.replace(/nn(?=\W|$)/gi, 'ⁿ');

            // nnh → hⁿ (干焦 nnh 佇音節尾的時才會換)
            text = text.replace(/nnh(?=\W|$)/gi, 'hⁿ');

            // ing → eng
            text = text.replace(/ing/gi, function(match) { return (match[0] === 'I' ? 'E' : 'e') + 'ng'; });
            text = text.replace(/íng/gi, function(match) { return (match[0] === 'Í' ? 'É' : 'é') + 'ng'; });
            text = text.replace(/ìng/gi, function(match) { return (match[0] === 'Ì' ? 'È' : 'è') + 'ng'; });
            text = text.replace(/îng/gi, function(match) { return (match[0] === 'Î' ? 'Ê' : 'ê') + 'ng'; });
            text = text.replace(/ǐng/gi, function(match) { return (match[0] === 'Ǐ' ? 'Ẽ' : 'ẽ') + 'ng'; });
            text = text.replace(/īng/gi, function(match) { return (match[0] === 'Ī' ? 'Ē' : 'ē') + 'ng'; });
            text = text.replace(/i̋ng/gi, function(match) { return (match[0] === 'I' ? 'Ě' : 'ě') + 'ng'; });

            // ik → ek
            text = text.replace(/ik/gi, function(match) { return (match[0] === 'I' ? 'E' : 'e') + 'k'; });
            text = text.replace(/i̍k/gi, function(match) { return (match[0] === 'I' ? 'E̍' : 'e̍') + 'k'; });

            text = text.replace(/[Aa]̋/g, function(match) { return match[0] === 'A' ? 'Ă' : 'ă'; });
            text = text.replace(/[Ee]̋/g, function(match) { return match[0] === 'E' ? 'Ĕ' : 'ĕ'; });
            text = text.replace(/[Ii]̋/g, function(match) { return match[0] === 'I' ? 'Ĭ' : 'ĭ'; });
            text = text.replace(/[Őő]/g, function(match) { return match[0] === 'Ő' ? 'Ŏ' : 'ŏ'; });
            text = text.replace(/[Űű]/g, function(match) { return match[0] === 'Ű' ? 'Ŭ' : 'ŭ'; });
            text = text.replace(/[Mm]̋/g, function(match) { return match[0] === 'M' ? 'M̆' : 'm̆'; });
            text = text.replace(/[Nn]̋g/g, function(match) { return match[0] === 'N' ? 'N̆g' : 'n̆g'; });

            text = text.replace(/[Ǎǎ]/g, function(match) { return match[0] === 'Ǎ' ? 'Ã' : 'ã'; });
            text = text.replace(/[Ěě]/g, function(match) { return match[0] === 'Ě' ? 'Ẽ' : 'ẽ'; });
            text = text.replace(/[Ǐǐ]/g, function(match) { return match[0] === 'Ǐ' ? 'Ĩ' : 'ĩ'; });
            text = text.replace(/[Ǒǒ]/g, function(match) { return match[0] === 'Ǒ' ? 'Õ' : 'õ'; });
            text = text.replace(/[Ǔǔ]/g, function(match) { return match[0] === 'Ǔ' ? 'Ũ' : 'ũ'; });
            text = text.replace(/[Mm]̌/g, function(match) { return match[0] === 'M' ? 'M̃' : 'm̃'; });
            text = text.replace(/[Ňň]g/g, function(match) { return match[0] === 'Ň' ? 'Ñg' : 'ñg'; });

            node.nodeValue = text;
        } else if (node.nodeType === Node.ELEMENT_NODE) {
            // 排除切換揤鈕
            if (node.id !== 'poj-toggle-btn') {
                for (let i = 0; i < node.childNodes.length; i++) {
                    applyPOJTransformation(node.childNodes[i]);
                }
            }
        }
    }

    // 共原始文字囥倒轉去
    function restoreOriginalText(node) {
        if (node.nodeType === Node.TEXT_NODE) {
            if (originalTextMap.has(node)) {
                node.nodeValue = originalTextMap.get(node);
            }
        } else if (node.nodeType === Node.ELEMENT_NODE) {
            // 共切換按鈕跳過
            if (node.id !== 'poj-toggle-btn') {
                for (let i = 0; i < node.childNodes.length; i++) {
                    restoreOriginalText(node.childNodes[i]);
                }
            }
        }
    }

    // 處理動態載入的內容
    function setupMutationObserver() {
        const observer = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if (mutation.addedNodes && mutation.addedNodes.length > 0) {
                    for (let i = 0; i < mutation.addedNodes.length; i++) {
                        const node = mutation.addedNodes[i];
                        // 共切換按鈕跳過
                        if (node.id === 'poj-toggle-btn') continue;

                        // 若這馬咧用 POJ,就使用轉換
                        if (usePOJ) {
                            applyPOJTransformation(node);
                        }
                    }
                }
            });
        });

        observer.observe(document.body, { childList: true, subtree: true });
    }

    // 頁面載入的時陣原初化
    window.addEventListener('load', function() {
        createToggleButton();
        setupMutationObserver();

        // 根據使用者的選擇來決定頭起先顯示的方式
        if (usePOJ) {
            applyPOJTransformation(document.body);
        }
    });
})();