owowify website

owowify website evewywhewe! >w<

目前為 2024-01-28 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        owowify website
// @description owowify website evewywhewe! >w<
// @author      owowed
// @version     0.0.2
// @namespace   fun.owowed.moe
// @license     GPL-3.0-or-later
// @match       *://*/*
// @grant       none
// @run-at      document-end
// @copyright   All rights reserved. Licensed under GPL-3.0-or-later. View license at https://spdx.org/licenses/GPL-3.0-or-later.html
// ==/UserScript==

/**
 * [author note]
 * 
 * honestly, i dont know what the fuck is going on here
 * 
 * this is one of my old personal userscript collection
 * 
 * i made this back when i began learning javascript and
 * userscript stuff, and also when i dont know any better
 * and start writing random stuff, like this
 * 
 * so yeah, expect some really bad poopy programmer code here
 * 
 * anyway, this userscript uses subtree mutation observer,
 * sooo that may degrade your browsing performance, beware
 * 
 * also, good luck when you want to fork it lol
 * 
 * ps: maybe sooner or later, ill clean this stuff up,
 * and rework some of the bits and stuff later
 * 
 * ~~ owowed
 */

/**
 * wincesed undew GNYU! GPL, vewsion 3.0 -w-
 */

const textNodeParentElementBlacklistSelector = "style, script, svg, noscript, iframe, object, code, pre, input";

let lastWindowHref = window.location.href;
let idleTimeout = 0;
{
    initObserver(document.body, {childList: true, subtree: true});

    owowifyElement(document.body);
    
    setInterval(() => {
        if (idleTimeout > 15) {
            if (lastWindowHref != window.location.href) {
                idleTimeout = 0;
                lastWindowHref = window.location.href;
            }
            else {
                return;
            }
        }
        
        owowifyElement(document.body);

        idleTimeout++;
    }, 1000);
    
    document.title = owowify(document.title);
}

function initObserver(node, options = {}) {
    const observer = new MutationObserver(records => {
        for (const mut of records) {
            if (mut.addedNodes.length > 0) {
                for (const nd of mut.addedNodes) {
                    owowifyElement(nd);
                    idleTimeout = 12;
                }
            }
        }
    });

    observer.observe(node, {...options});
}

/** @param {Element} element */
function owowifyElement(element) {
    if (element.nodeType != Node.ELEMENT_NODE) return;

    const currentElementValid =
        (isElementValid(element) && isElementValid(element.parentElement))
        || (element instanceof HTMLAnchorElement && !element.textContent.includes(element.href));
    if (!currentElementValid) {
        element.noOwowify = true;
    }

    if (element.hasAttribute("title")) {
        element.setAttribute("title", owowify(element.getAttribute("title")));
    }

    for (const nd of element.childNodes) {
        if (nd.nodeType == Node.ELEMENT_NODE) {
            owowifyElement(nd);
        }
        if (nd.nodeType == Node.TEXT_NODE && currentElementValid) {
            owowifyNode(nd);
        }
    }

    if (element instanceof HTMLInputElement) {
        if (element.readOnly && !["password", "url", "hidden"].includes(element.type)) {
            element.value = owowify(element.value);
            element.placeholder = owowify(element.placeholder);
        }
    }
}

/** @param {Node} node */
function owowifyNode(node) {
    if (node.nodeType == Node.TEXT_NODE) {
        node.nodeValue = owowify(node.nodeValue);
    }
}

function isElementValid(element) {
    if (element == null) return null;
    return !(element.matches(textNodeParentElementBlacklistSelector)
        || element.noOwowify
        || element.classList?.contains?.("-owo-no-owowify")
        || element.isContentEditable);
}

/**
 * 
 * @param {String} textstr 
 * @param {OwowifyOptions} options 
 * @returns {String}
 */
function owowify(textstr, options) {
    const endSentencePattern = String.raw`([\w ,.!?]+)?`; // endSentencePattern
    const endSentencePattern1 = String.raw`([\w ,.?]+)?`; // endSentencePattern without "!" sign
    const endSentencePattern2 = String.raw`([\w ,.]+)?`; // endSentencePattern without "!" and "?" sign
    const vowel = "[aiueo]";
    const vowelNoE = "[aiuo]"; // vowel without e
    const vowelNoIE = "[auo]"; // vowel without i and e
    const zackqyWord = "[jzckq]";

    let result = Array.isArray(textstr) ? textstr.join(" ") : textstr;

    // OWO EMOTE
    // result = subOwoEmote(result, reg`/(stop\!+)${endSentencePattern1}/gi`, " -M-");
    result = subOwoEmote(result, reg`/(i(?:'|)m(?:\s+|\s+so+\s+)bored)${endSentencePattern}/gi`, " -w-")
    result = subOwoEmote(result, reg`/(love\s+(?:you|him|her|them))${endSentencePattern}/gi`, " uwu");
    result = subOwoEmote(result, reg`/(i\s+don(?:'|)t\s+care|i\s*d\s*c)${endSentencePattern}/gi`, " 0w0");
    // result = subOwoEmote(result, reg`/(i(?:'|)m\s+scared)${endSentencePattern}/gi`, " ;w;");

    // WORD REPLACEMENT
    result = result.replace(reg`/l${vowel}ve?/gi`, g0 => subSameCase(g0, "luv"));
    
    // MAIN TRANSLATION
    result = result.replace(/r/gi, g0 => subSameCase(g0, "w"));
    // result = result.replace(reg`/(?<=(?<!w)(?<!${vowel}+)${vowel}+)l(?!l+\s+|l+$)|l(?=${vowel}+(?!${vowel}+)(?!w))/gi`, g0 => subSameCase(g0, "w")); // nope
    result = result.replace(reg`/(?<=(?<!w${vowel}+)${vowel}+)l(?!l+)/gi`, g0 => subSameCase(g0, "w"));
    result = result.replace(reg`/n(${vowelNoE}+)/gi`, (g0, g1) => subSameCase(g0 + g1, `ny${g1}`));
    result = result.replace(reg`/m(${vowelNoIE}+)(?!w*${zackqyWord})/gi`, (g0, g1) => subSameCase(g0 + g1, `my${g1}`));
    result = result.replace(reg`/p(${vowelNoIE}+)(?!w*${zackqyWord})/gi`, (g0, g1) => subSameCase(g0 + g1, `pw${g1}`));

    return result;
}

/**
 * @param {string} all 
 * @param {RegExp} pattern 
 * @param {string} emote 
 */
function subOwoEmote(all, pattern, emote) {
    const matchEndSpace = /^\s+$/g;
    return all.replace(pattern, (gp0, gp1, gp2) => {
        return gp2 == undefined || matchEndSpace.test(gp2)
            ? gp1 + emote
            : gp0;
    });
}

/** 
 * @param {string} strA
 * @param {string} strB
 */
function subSameCase(strA, strB) {
    let result = "";

    for (let i = 0; i < strB.length; i++) {
        if (strA[i] != undefined && strB[i] != undefined) {
            if (strA[i].toUpperCase() == strA[i]) {
                result += strB[i].toUpperCase();
            }
            else if (strA[i].toLowerCase() == strA[i]) {
                result += strB[i].toLowerCase();
            }
            else {
                result += strB[i];
            }
        }
        else {
            result += strB[i];
        }
    }

    return result;
}

/** @param {[string[], ...any[]]} templateArgs */
function reg(...templateArgs) {
    const rawString = String.raw(...templateArgs);
    const pattern = rawString.substring(1, rawString.lastIndexOf("/"));
    const flags = rawString.substring(rawString.lastIndexOf("/")+1, rawString.length);

    return new RegExp(pattern, flags);
}

// mdn docs
/** @param {string} str */
function regexEscape(str) {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}