camconn / Stump Script

// ==UserScript==
// @name Stump Script
// @description Make the web great again!
// @author camconn
// @version 1.0.10
//
// @copyright 2016, Cameron Conn (https://www.camconn.cc/)
// @license GPL-3.0
// @homepageURL https://github.com/camconn/stumpscript/
// @supportURL https://github.com/camconn/stumpscript/issues/
// @updateURL https://openuserjs.org/meta/camconn/Stump_Script.meta.js
//
// @include http*
// @include https*
// @include file*
//
// @namespace https://www.camconn.cc/
// @grant none
// @run-at document-end
// ==/UserScript==

/*
 * Stump Script - Make the Web Great Again!
 * Copyright (C) 2016   Cameron Conn
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

"use strict";

/*
 * This is where the magic happens.
 *
 * For some Regexes (for example the two #makedonalddrumpfagain matches
 * in the first section below) there are two nearly-duplicate entries.
 * These are duplicated because twitter doesn't play nice with hashtags,
 * and doesn't include the hashtag in the *actual* text content, and
 * just shows it with some CSS trickery.
 */

var MATCHES = [
// Things cucks say (well, thats everything in this array, you get the idea)
[/\bTrumpkin/gi, "Nimble Navigator"], [/\bDrumpf/gi, "The God-Emperor"], [/\bTrumpeteer/gi, "centipede"], [/\bTrumpies?/gi, "centipede"], [/\b(Donald )?Trump (Supporter|Fan)/gi, "Nimble Navigator"], [/\b\#makedonalddrumpfagain/gi, "#IAmACuck"], [/\bmakedonalddrumpfagain/gi, "IAmACuck"], [/\b\#NeverTrump/gi, "#IHateAmerica"], [/\bNeverTrump/gi, "IHateAmerica"], [/\b\#Dump(Trump|Drumpf?)/gi, "ImAChump"], [/\bDump(Trump|Drumpf?)/gi, "ImAChump"],

// El Rato
[/\bLyin.? Ted Cruz/gi, "The Lyin' Rat"], [/\bTed Cruz/gi, "El Rato"], [/\bcruz (supporter|fan)s?/gi, "Cruzcucks"], [/\b#TedCruz/gi, "#ElRato"], [/\bTedCruz/gi, "ElRato"], [/\b\#cruz2016/gi, "#ElRato2016"], [/\bcruz2016/gi, "ElRato2016"], [/\b\#choosecruz/gi, "#ZodiacKiller"], [/\bchoosecruz/gi, "ZodiacKiller"], [/\b\#cruzcrew/gi, "#RatAttack"], [/\bcruzcrew/gi, "RatAttack"],

// Low Energy
[/\b(Jeb|John Ellis) Bush/gi, "The Foolish Guac Merchant"], [/\bJeb\!?/gi, "Yeb!"],

// Golly Gee Guy
[/\bOhio Gov(ernor|\.?) (John )?Kasich/gi, "Golly Gee Guy"], [/\bJohn Kasich/gi, "Literally Who"], [/\bKasich/gi, "Cucksich"],

// Rubot
[/\bMarco Rubio/gi, "the Choke Artist"], [/\b(Candidate|Senator) (Marco )?Rubio/gi, "the Foolish Foam Boi"], [/\bRubio/gi, "Rubot"],

// Bernouts
[/\bBernie ([\'\"]?Bernard[\'\"]? )?Sanders/gi, "Bolshevik Bernie"], [/\bBernie[\- ]?Bros/gi, "Berniebots"], [/\bBernie[\- ]?Bro/gi, "Berniebot"], // Yes, its a duplicate but it matches the plural form!
[/\bFeel(ing)? The Bern/gi, "Help Im Berning"], [/\bFree College/gi, "Free Coll Edge"], [/\b\#FeelTheBern/gi, "#HelpImBerning"], [/\bFeelTheBern/gi, "HelpImBerning"], [/\b\#Bernie2016/gi, "#BernVictim"], [/\bBernie2016/gi, "BernVictim"], [/\b\#Birdie2016/gi, "#Bernout"], [/\bBirdie2016/gi, "Bernout"],

// $hillary
[/\bHRC/gi, "$hillary"], [/\bHillary (D\.? |Diane )?(R\.? |Rodham )?Clinton/gi, "Shillary"], [/\bHillary (Clinton(\'s)? )?(Super)?PAC/gi, "Shillary's PAC"], [/\bSecretary (Hillary )?Clinton/gi, "Secretary Shillary"], [/\b\#Hillary(Clinton)?/gi, "#Shillary"], [/\bHillary(Clinton?)/gi, "Shillary"],

// British Cuck
[/\bJohn Oliver/gi, "Current Year Man"], [/\bBecause It'?s [0-2][0-9]{3}\b/gi, "Because Current Year"], [/\bIt'?s [1-2][0-9]{3}\b/gi, "It's Current Year"],

// Establishment
[/\bGOP( Establishment|e)/gi, "cuckservatives"], [/r\/conservative/gi, "r/cuckservative"],
// [new RegExp(/\bground[- ]game/gi, "corruption"], // Conflicts too much with muh sports
[/\bGlenn (Lee )?Beck/gi, "Glenn Cuck"], [/\bGlennBeck/gi, "GlennCuck"], [/TheBlaze/gi, "TheCuck"],

// Terrorism/Islam
[/\b(ISIS|ISL\b|ISIL\b|Daesh|(Islamic State of Iraq and the Levant))/gi, "Islamic State of Idiotic Savages"], [/\bSyrian (migrant|refugee|immigrant)/gi, "Syrian totally-not-terrorist"], [/\bReligion of Peace/gi, "Religion of Pieces"], [/\b(Kaaba|Kabah)/gi, "stupid rock"], [/\bmosque/gi, "terrorist recruiting center"],

// Bad Guys
[/\b(George )?Soros/gi, "the Sith Lord"], [/\bTrump protesters/gi, "some losers"], [/\b(Mark )?Zuckerberg/gi, "Mark Cuckerberg"], [/\bundocumented (citizen|worker)/gi, "snake"]];

// CASES constants - these represent what the source text's casing is.
var UNKNOWN = 0; // UnKnOwN (or any other variation)
var UPPER = 1; // UPPERCASE
var LOWER = 2; // lowercase
var TITLE = 3; // TitleCase

/**
 * Get the Title Case for a string.
 *
 * Modified from http://stackoverflow.com/a/196991
 *
 * @param {text} The string to convert to Title Case.
 * @return The string in Title Case.
 */
function titleCase(text) {
	return text.replace(/([^\W_]+[^\s-]*) */g, function (s) {
		return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase();
	});
}

/**
 * Determine the type of capitalization a string uses. Returns one of the
 * case constants available.
 *
 * @param {text} The string to detect the casing of.
 * @return What type of casing the text uses (see CASES constants for information).
 */
function caseType(text) {
	var possibleUppercase = true;
	var possibleLowercase = true;
	var possibleTitlecase = true;

	var prevChar = null;

	var _iteratorNormalCompletion = true;
	var _didIteratorError = false;
	var _iteratorError = undefined;

	try {
		for (var _iterator = text[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
			var c = _step.value;


			if (/[A-Za-z]/.test(c)) {
				var isUpper = c === c.toUpperCase();
				if (isUpper) possibleLowercase = false;else possibleUppercase = false;

				if (prevChar === null || /[\s\-_\+\.]/.test(prevChar)) {
					if (!isUpper) possibleTitlecase = false;
				} else if (isUpper) {
					possibleTitlecase = false;
				}
			}

			prevChar = c;
		}
	} catch (err) {
		_didIteratorError = true;
		_iteratorError = err;
	} finally {
		try {
			if (!_iteratorNormalCompletion && _iterator.return) {
				_iterator.return();
			}
		} finally {
			if (_didIteratorError) {
				throw _iteratorError;
			}
		}
	}

	if (possibleTitlecase) return TITLE;
	if (possibleLowercase) return LOWER;
	if (possibleUppercase) return UPPER;

	return UNKNOWN;
}

/**
 * Helper method for string replacement according to capitalization type.
 *
 * @param {original}    The original text to be replaced.
 * @param {replacement} The replacement text.
 * @return The replacement text in the appropriate casing.
 */
function replaceAppropriateCase(original, replacement) {
	var sourceCase = caseType(original);

	switch (sourceCase) {
		case UPPER:
			return replacement.toUpperCase();
		case LOWER:
			return replacement.toLowerCase();
		case TITLE:
			return titleCase(replacement);
		case UNKNOWN:
			return replacement;
	}
}

/**
 * Perform a "walk" through all of the children of this node and apply the
 * necessary text substitution.
 *
 * Adapted from Mozilla Developer Network:
 * https://developer.mozilla.org/en-US/docs/Web/API/Node#Browse_all_child_nodes
 *
 * @param {oParent} The Node to walk through.
 */
function walk(oParent) {
	for (var node = oParent.firstChild; node; node = node.nextSibling) {
		walk(node);
	}if (oParent.nodeType === Node.TEXT_NODE) {
		var initialText = oParent.textContent + "";

		if (initialText.length > 0) {
			var newText = transformText(initialText);

			if (initialText !== newText) {
				oParent.textContent = newText;
			}
		}
	}
}

/**
 * Replaces the text in text with more *appropriate* text.
 * @param {text} The text to transform.
 * @return The transformed text.
 */
function transformText(text) {
	// console.log("before: " + text)

	var _iteratorNormalCompletion2 = true;
	var _didIteratorError2 = false;
	var _iteratorError2 = undefined;

	try {
		var _loop = function _loop() {
			var pair = _step2.value;

			text = text.replace(pair[0], function (r) {
				return replaceAppropriateCase(r, pair[1]);
			});
		};

		for (var _iterator2 = MATCHES[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
			_loop();
		}

		// console.log("after: " + text)
	} catch (err) {
		_didIteratorError2 = true;
		_iteratorError2 = err;
	} finally {
		try {
			if (!_iteratorNormalCompletion2 && _iterator2.return) {
				_iterator2.return();
			}
		} finally {
			if (_didIteratorError2) {
				throw _iteratorError2;
			}
		}
	}

	return text;
}

// Cache any nodes we modify so we don't accidentially mess with nodes twice
var nodeCache = [];

/**
 * Callback for modern MutationObserver API. This is used as implementing the
 * newer DOM4 standards.
 */
function mutationHandler(mutations) {
	mutations.forEach(function (m) {
		// Don't modify a node twice in a row
		var index = nodeCache.indexOf(m);
		if (index !== -1) {
			nodeCache.splice(index, 1);
			return;
		}

		walk(m.target);

		m.addedNodes.forEach(function (n) {
			nodeCache.push(n);walk(n);
		});

		// Store this node in temporary cache
		nodeCache.push(m.target);
	});
}

if (MutationObserver) {
	var observer = new MutationObserver(mutationHandler);
	observer.observe(window.document.body, { childList: true, attributes: true });
} else {
	// shim code in case DOM4 APIs aren't supported
	window.document.body.addEventListener("DOMNodeInserted", function (ev) {
		walk(ev.target);
	}, false);
}

var oldTitle = window.document.title + "";
window.document.title = transformText(oldTitle);

walk(window.document.body);