better.ts 9.71 KB
interface ReplacementFunction {
    (match: string): string;

interface Substitution {
    search: string | RegExp;
    replace: string | ReplacementFunction;

let BETTER = (function () {
    'use strict';

    let subs: Substitution[] = [];
    let score: number = 0;
    let result = document.createElement('div');

    let applySubs: (node: Node) => void = function (node) {
        let original: string, replacement: string, i: number;

        // remove whitespace runs to improve regex accuracy
        original = node.nodeValue.replace(/\s+/gm, ' ');
        replacement = original;

        // skip bare URLs
        if (original.indexOf('http') === 0) {

        for (let sub of subs) {
            replacement = replacement.replace(<any>, <any>sub.replace);

        if (original !== replacement) {
            node.nodeValue = replacement;
            score += 1;

    let sub: (search: string | RegExp, replace?: string | ReplacementFunction) => void = function (search, replace = '') {
        subs.push({search: search, replace: replace});

    let uppercaseFirstGroup: (match: string, p1?: string) => string = function (match, p1) {
        return p1.toUpperCase();

    let uppercaseSecondGroup: (match: string, p1?: string, p2?: string) => string = function (match, p1, p2) {
        return p2.toUpperCase();

    let caseAwareReplace: (replacement: string) => ReplacementFunction = function (replacement = '') {
        return function (match: string) {
            if (/^[A-Z]/.test(match)) {
                return replacement.charAt(0).toUpperCase() + replacement.slice(1);
            return replacement;

    // regrettable phrases
    sub(/kudos/gi, caseAwareReplace('congratulations'));
    sub(/webizens/gi, caseAwareReplace('people'));
    sub(/i (\w+ )?suspect( that)?/gi, caseAwareReplace('i think'));
    sub(/(\W)folks(\W)/g, '$1people$2');
    sub(/the bee'?s knees/gi, 'great');
    sub(/indeed\W+([a-z])/gi, '$1');
    sub(/\bgee\W+(\w)/gi, uppercaseFirstGroup);
    sub(/a heck\Wof\Wa\Wlot/gi, 'a lot');
    sub(/\b(the )?heck\W+(\w)/gi, uppercaseSecondGroup);
    sub(/as a synecdoche/gi, 'as a figure of speech');
    sub(/\byep\W+(\w)/gi, uppercaseFirstGroup);
    sub(/\bmodulo\b/gi, caseAwareReplace('aside from'));
    sub(/I wish to/gi, 'I');
    sub(/\W*alas[, ]?/gi);
    sub('guys and gals', 'everyone');
    sub(/\bin situ\b/g, 'in place');
    sub(/\bhyperbole\b/g, 'exaggeration');
    sub(/\bkiddos\b/g, 'kids');
    sub('netizen', 'user of the Internet');
    sub('for ages', 'for a long time');
    sub(/\bruh roh\W+/gi);
    sub(/\btuts\b/i, 'tutorials');
    sub(/^wh?elp\W+(\w)/gi, uppercaseFirstGroup);
    sub(/\byup\b/gi, caseAwareReplace('yes'));
    sub(/\b(oh )?gosh\b\W+/gi, '');
    sub(/relevant xkcd\s*:?\s*/i);
    sub(/\bI concur\b/gi, 'I agree');
    sub(/\bergo\b/gi, 'so');
    sub(/\bI'm keen on/gi, 'I\'m interested in');
    sub(/Citation needed\.\s*/gi, '');

    sub(/I personally/g, 'I');
    sub(/\bmost any\b/gi, caseAwareReplace('almost any'));
    sub(/(, )?y'all/gi, '');
    sub(/\bPeriod\.\s+/g, '');
    sub(/\bapologia\b/g, 'apology');
    sub(/So I/, 'I');

    // tone it down Captain Emo
    sub(/why oh why/gi, caseAwareReplace('why'));
    sub(/a thousand times yes/gi);
    sub(/yes(,? yes)+/gi, caseAwareReplace('yes'));
    sub(/\bugh\W+(\w)/gi, uppercaseFirstGroup);
    sub(/the fact is( that)?\s*([a-z])/gi, uppercaseSecondGroup);
    sub(/(just )?can't (.*?)fathom\b/gi, 'can\'t see');
    sub(/(just )?can\s?not (.*?)fathom\b/gi, 'can\'t see');
    sub(/\bunfathomable\b/g, caseAwareReplace('staggering'));
    sub(/\bhow on earth\b/gi, caseAwareReplace('how'));
    sub(/\bwhat the hell\b/gi, caseAwareReplace('what'));
    sub(/\bwhy the hell\b/gi, caseAwareReplace('why'));
    sub(/\bsure as hell\b/gi);
    sub('very much like', 'like');
    sub('I literally', 'I');
    sub('I strongly', 'I');
    sub('I really', 'I');
    sub('I respectfully', 'I');
    sub(/(really\s*)+/g, caseAwareReplace('really '));
    sub(/I'm sorry[, ]+but\s+/i);
    sub(/Never again\.?\s*/)

    // you're not the boss of me
    sub(/\s?end of discussion\W+/gi);
    sub(/\s?knock it off\W+/gi);
    sub(/\(think .*?\)\s+/gi);

    // laziness
    sub(/c'mon/gi, caseAwareReplace('come on'));
    sub('kinda', 'kind of');
    sub(/gotta/gi, caseAwareReplace('have to'));
    sub(/github/gi, 'GitHub');
    sub(/\bwanna\b/gi, caseAwareReplace('want to'));
    sub(/\bnah\b/gi, caseAwareReplace('no'));
    sub(/\bpls\b/gi, caseAwareReplace('please'));
    sub(/(\w)\s*!=+\s*(\w)/g, '$1 is not the same as $2');
    sub(/\bdunno\b/g, 'don\'t know');
    sub(/ re: /g, ' regarding ');
    sub(/(\W)w\\\/\s*/gi, '$1 with ');

    // we are adults
    sub('a$$', 'ass');
    sub('f-up', 'fuckup');
    sub('f*cking', 'fucking');
    sub(/fri[ckgi]+n?\W+/, '');
    sub(/\bef+ing?\W+/g, ' ');
    sub(/\bicky\b/gi, caseAwareReplace('bad'));

    // emoticons

    // acronyms and shorthand
    sub(/\bafaik\b/gi, caseAwareReplace('as far as I know'));
    sub(/\birc\b/gi, 'IRC');
    sub(/IMO, (\w)/g, uppercaseFirstGroup);
    sub(/\bTIL /g, '');
    sub(/^IME/g, 'In my experience');
    sub(/\bbtw\b/gi, caseAwareReplace('by the way'));
    sub(/w\/e/gi, caseAwareReplace('whatever'));
    sub(/\btfa\b/gi, caseAwareReplace('the article'));
    sub(/\bgtfo\b/gi, 'get out');
    sub(/\bymmv(, but) (\w)/gi, uppercaseSecondGroup);
    sub(/,?( so)? ymmv/gi);
    sub(/\bianal\b/gi, 'I am not a lawyer');
    sub(/\biq\b/g, 'IQ');
    sub(/\bIMHO\W+(\w)/g, uppercaseFirstGroup);
    sub(/\bLPT[\w\W]+:\s*(\w)/g, uppercaseFirstGroup);
    sub(/\bw\//, 'with');
    sub(/\bFWIW,?\s*(\w)/, uppercaseFirstGroup);

    // get to the point
    sub(/I (up|down)voted you\.\s*/g);
    sub(/I (up|down)voted you\W+because (\w)/gi, uppercaseSecondGroup);
    sub(/I think that[ 'i]+s/g, caseAwareReplace('that\'s'));
    sub(/^I think that\W+(\w)/g, uppercaseFirstGroup);
    sub(/^I think (that )?(\w)/g, uppercaseSecondGroup);
    sub(/\b[uh]m+\W+(\w)/gi, uppercaseFirstGroup);
    sub(/\beep\W+(\w)/gi, uppercaseFirstGroup);
    sub(/^Hell,\W+(\w)/g, uppercaseFirstGroup);
    sub(/^Ah\W+(\w)/gi, uppercaseFirstGroup);
    sub(/^Ok\W+so\W+(\w)/gi, uppercaseFirstGroup);
    sub(/^well\W+(\w)/gi, uppercaseFirstGroup);
    sub(/^Seems those/g, 'Those');
    sub(/\bI for one\W+/g, 'I ');
    sub(/\bI'm not sure I\b/g, 'I don\'t');
    sub(/^You see\W+(\w)/g, uppercaseFirstGroup);
    sub('Sound familiar?');
    sub(/\bI'm curious (to know)?\W+(\w)/g, uppercaseSecondGroup);
    sub(/\bfirst of all\W+(\w)/gi, uppercaseFirstGroup);
    sub(/^You should (\w)/g, uppercaseFirstGroup);
    sub(/^\w+ here, (\w)/g, uppercaseFirstGroup);
    sub(/I don't know about you\W*but\s*/, '');
    sub(/\W*in my opinion\W*/g, ' ');
    sub(/In my opinion\W+(\w)/g, uppercaseFirstGroup);
    sub(/A thousand times this\W+/gi);
    sub(/As a .*?I/g, 'I');
    sub(/Meh[.,]\s+(\w)/g, uppercaseFirstGroup);
    sub(/To be fair, (\w)/g, uppercaseFirstGroup);
    sub(/I'll be honest, (\w)/g, uppercaseFirstGroup);
    sub(/That'?s true,? but (\w)/g, uppercaseFirstGroup);

    // corruptions of the language
    sub(/gonna/gi, caseAwareReplace('going to'));
    sub(/\bu\b/gi, caseAwareReplace('you'));
    sub(/\bconvos\b/gi, caseAwareReplace('conversations'));
    sub(/\bconvo\b/gi, caseAwareReplace('conversation'));
    sub(/\bCause then/g, 'Because then');
    sub(/\bs?he\s*\/\s*s?he/gi, 'he or she');
    sub(/ i've a /gi, ' I have a ');
    sub(/\bkiddos\b/, 'kids');
    sub(/\bkiddo\b/, 'kid');

    // typography
    sub(/\s*\.{2,3}/g, '… ');              // horizontal ellipsis
    sub(/(\w)\s*-{2,3}\s*(\w)/g, '$1—$2'); // double or triple hypens to mdash
    sub(' - ', '—');                       // lone hypen to mdash
    sub(/\s?,\s*(\w)/gi, ', $1');          // space around comma
    sub(/\.([A-Z]\w+)/g, '. $1');          // space after period
    sub(/_+([^_]+)_+/g, '$1');             // markdown-style italics
    sub(/\s+\./, '.');                     // no space before period

                                           // common typos
    sub(/\bteh\b/gi, 'the');

                                           // cleanup
    sub(/,\s*\./g, '.');                   // comma at end of sentence
    sub(/\s+/g, ' ');

    function walk (root: Node) {
        let treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
        while (treeWalker.nextNode()) {


    result.setAttribute('style', [
        'position: fixed',
        'top: 0',
        'right: 0',
        'width: 250px',
        'height: 1em',
        'background-color: orange',
        'color: #000',
        'font:12px sans-serif',
        'text-align: center',
        'padding: 1em',
        'box-shadow: 7px 7px 5px 0px rgba(50, 50, 50, 0.75)'

    if (score === 0) {
        result.innerHTML = 'This page could not be made any better.';
    } else {
        result.innerHTML = `Made the page better in ${score} ${score === 1 ? 'place' : 'places'}`;


    setTimeout(function () {
    }, 5000);

    return {
        walk: walk