- // ==UserScript==
- // @name owowify website
- // @description owowify website evewywhewe! >w<
- // @author owowed
- // @version 0.0.3
- // @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==
-
- /**
- * wincesed undew GNYU! GPL vewsion 3.0 -w-
- */
-
- const blocklistTags = "style, script, svg, noscript, iframe, object, code, pre, input";
-
- !function () {
- const updateSubtreeOwowification = () => {
- setTimeout(() => owowifySubtree(document.body), 700);
- };
- const windowHrefCheck = stateCheck(
- () => window.location.href,
- () => updateSubtreeOwowification()
- );
- const documentTitleCheck = stateCheck(
- () => owowify(document.title)
- );
-
- const observer = new MutationObserver((records) => {
- if (windowHrefCheck()) return;
-
- documentTitleCheck();
-
- for (const record of records) {
- for (const newNode of record.addedNodes) {
- owowifySubtree(newNode);
- }
- }
- });
-
- observer.observe(document.body, {
- childList: true,
- subtree: true,
- /* don't use characterData, it'll kill your page */
- });
-
- owowifySubtree(document.body);
- document.title = owowify(document.title);
-
- function stateCheck(newValueGetter, callback) {
- let value = newValueGetter;
-
- return () => {
- const newValue = newValueGetter();
- if (value != newValue) {
- value = newValue;
- callback?.();
- return true;
- }
- };
- }
- }();
-
- /** @param {Element} element */
- function owowifySubtree(element) {
- if (element.nodeType != Node.ELEMENT_NODE) return;
-
- const currentElementValid =
- isElementValidOwowify(element) &&
- isElementValidOwowify(element.parentElement);
-
- if (!currentElementValid) {
- element.noOwowify = true;
- }
-
- if (element instanceof HTMLInputElement) {
- if (element.readOnly && !["password", "url", "hidden"].includes(element.type)) {
- element.value = owowify(element.value);
- element.placeholder = owowify(element.placeholder);
- }
- }
-
- if (element.hasAttribute("title")) {
- element.setAttribute("title", owowify(element.getAttribute("title")));
- }
-
- for (const node of element.childNodes) {
- if (node.nodeType == Node.ELEMENT_NODE) {
- owowifySubtree(node);
- }
- if (node.nodeType == Node.TEXT_NODE && currentElementValid) {
- owowifyTextNode(node);
- }
- }
- }
-
- /** @param {Node} node */
- function owowifyTextNode(node) {
- if (node.nodeType == Node.TEXT_NODE) {
- node.nodeValue = owowify(node.nodeValue);
- }
- }
-
- /** @param {Element} element */
- function isElementValidOwowify(element) {
- if (element == null) return null;
- if (element.noOwowify || element.classList?.contains?.("-owo-no-owowify")) return false;
- if (element instanceof HTMLInputElement) {
- return !element.textContent.includes(element.href);
- }
- return !(element.matches(blocklistTags) || element.isContentEditable);
- }
-
- /**
- * @param {String} inputText
- * @returns {String}
- */
- function owowify(inputText) {
- 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]";
-
- const result = inputText
- // OwO emote
- .replace(reg`/(i(?:'|)m(?:\s+|\s+so+\s+)bored)${endSentencePattern}/gi`, subOwoEmote("-w-"))
- .replace(reg`/(love\s+(?:you|him|her|them))${endSentencePattern}/gi`, subOwoEmote("uwu"))
- .replace(reg`/(i\s+don(?:'|)t\s+care|i\s*d\s*c)${endSentencePattern}/gi`, subOwoEmote("0w0"))
- // world substitution
- .replace(reg`/l${vowel}ve?/gi`, $0 => subSameCase($0, "luv"))
- // OwO translation
- /* replace all "r" to "w", no exception! */
- .replace(/r/gi, $0 => subSameCase($0, "w"))
- /* lame -> wame, goal -> goaw, gallery -> gallewy, lol -> lol, null -> null */
- .replace(reg`/(?<!([wl]${vowel}+)|l)l(?!(${vowel}?l+)|.${vowel})/gi`, $0 => subSameCase($0, "w"))
- /* na -> nya, nu -> nyu, no -> nyo, ne -> nye */
- .replace(reg`/n(${vowelNoE}+)/gi`,
- ($0, $vowel) => subSameCase($0 + $vowel, `ny${$vowel}`))
- /* ma -> mya, mu -> myu, mo -> myo */
- .replace(reg`/m(${vowelNoIE}+)(?!w*${zackqyWord})/gi`,
- ($0, $vowel) => subSameCase($0 + $vowel, `my${$vowel}`))
- /* pa -> pwa, pu -> pwu, po -> pwo */
- .replace(reg`/p(${vowelNoIE}+)(?!w*${zackqyWord})/gi`,
- ($0, $vowel) => subSameCase($0 + $vowel, `pw${$vowel}`));
-
- return result;
- }
-
- function subOwoEmote(emote) {
- const matchEndSpace = /^\s+$/g;
-
- return ($0, $setenceBeforeEnd, $endSentence) => {
- if ($endSentence == undefined || matchEndSpace.test($endSentence)) {
- return `${$setenceBeforeEnd} ${emote}`;
- }
- else return $0;
- }
- }
-
- /**
- * @param {string} inputText
- * @param {string} replaceText
- */
- function subSameCase(inputText, replaceText) {
- let result = "";
-
- for (let i = 0; i < replaceText.length; i++) {
- if (inputText[i] != undefined && replaceText[i] != undefined) {
- if (inputText[i].toUpperCase() == inputText[i]) {
- result += replaceText[i].toUpperCase();
- }
- else if (inputText[i].toLowerCase() == inputText[i]) {
- result += replaceText[i].toLowerCase();
- }
- else {
- result += replaceText[i];
- }
- }
- else {
- result += replaceText[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
- }