Maonyman / Namize Background Ponies!

// ==UserScript==
// @name        Namize Background Ponies!
// @namespace   http://derpiboo.ru/images/namize_bp
// @description Give Background Ponies on Derpibooru their own names!
// @icon        https://u.smutty.horse/mbzdgrpesio.png
// @match       https://*.derpibooru.org/*
// @match       https://*.trixiebooru.org/*
// @match       https://*.ponybooru.org/*
// @version     1.054
// @inject-into content
// @noframes
// @license     MIT
// @require     https://openuserjs.org/src/libs/soufianesakhi/node-creation-observer.js
// ==/UserScript==

(function () {

/* Script settings--------------------------------------------------------------------------------- */

  // change this to false if you don't like colors
  const colored = true;

  // true -  full name will be coloured
  // false - only marker will be coloured
  const fullNameColor = true;

  // change this to false if you don't wish add " (the Background Pony)" to the end of anon's "names"
  const addBackgroundPony = false;

  // change this to false if you use a light theme on your boorus
  const darkTheme = true

/* ------------------------------------------------------------------------------------------------ */

  const SCRIPT_ID = 'namize_bp';

  const names1 = ['A Fucking', 'Aero', 'Air', 'Almond', 'Alpha', 'Amaranthine', 'Amber', 'American', 'Annoying', 'Apple', 'Apricot', 'Aqua', 'Asian', 'Astral', 'Awesome', 'Bad', 'Based', 'Beauty', 'Beige', 'Belle', 'Berry', 'Beta', 'Big', 'Bitter', 'Black', 'Blaze', 'Blazing', 'Blue', 'Bold', 'Bon', 'Boop the', 'Bright', 'Brisk', 'Broken', 'Bronze', 'Brown', 'Candy', 'Caramel', 'Careless', 'Carrot', 'Charming', 'Cherry', 'Chilly', 'Chimmy', 'Choco', 'Chocolate', 'Chromatic', 'Chubby', 'Citric', 'Classic', 'Clean', 'Clear', 'Clever', 'Cloudy', 'Cocky', 'Coco', 'Cold', 'Cool', 'Copper', 'Corky', 'Cosmic', 'Cranky', 'Crazy', 'Cringe', 'Cutie', 'Da’', 'Daring', 'Dark', 'Deadly', 'Delta', 'Derpy', 'Desert', 'Desired', 'Diamond', 'Dim', 'Dirty', 'Ditzy', 'Dizzy', 'DJ', 'Doctor', 'Double', 'Drama', 'Drunk', 'Dusk', 'Eastern', 'Easy', 'Emerald', 'Empress', 'Epic', 'European', 'Fancy', 'Fantastic', 'Filthy', 'Flash', 'Flirtatious', 'Fluffy', 'Flutter', 'Foggy', 'Full', 'Funny', 'Fuzzy', 'Gamma', 'Gandalf', 'General', 'Gentle', 'Ginger', 'Glass', 'Glitter', 'Golden', 'Good', 'Granny', 'Gray', 'Green', 'Half of a', 'Half Baked', 'Handsome', 'Happy', 'Hard', 'Hasty', 'Heavy', 'Hematite', 'High', 'Hitch', 'Honey', 'Horny', 'Hot', 'Hyper', 'Ice', 'Igneous', 'Iron', 'Ivory', 'Izzy', 'Jasper', 'Large', 'Last', 'Lauren', 'Lavender', 'Legendary', 'Lemony', 'Liberty', 'Light', 'Lightning', 'Lil\'', 'Lilac', 'Literally', 'Little', 'Lone', 'Long John', 'Lovely', 'Lucky', 'Main', 'Malachite', 'Metal', 'Meteor', 'Mini', 'Misty', 'Mixed', 'Mr.', 'Mrs.', 'Ms.', 'Moon', 'Mud', 'Multicolored', 'My Little', 'Mysterious', 'Mythical', 'Navy', 'Neck', 'Neon', 'Nightmare', 'Noisy', 'Northern', 'Nurse', 'Nyan', 'Old', 'Olive', 'Omega', 'Onyx', 'Orange', 'Paper', 'Party', 'Peachy', 'Pear', 'Pearl', 'Perfect', 'Pink', 'Pinkie', 'Pipp', 'Praise the', 'Princess', 'Professor', 'Proud', 'Pure', 'Purple', 'Quartz', 'Queen', 'Quick', 'Rainbow', 'Rainy', 'Really Really Really Really Really Really Really Really Really Really Really Long', 'Rrrrrrrrrreeeeeeeeeaaaaaaaaalllllllllyyyyyyyyy Ssssssssslllllllllooooooooowwwwwwwww', 'Red', 'Rosy', 'Ruby', 'Ruff', 'Sad', 'Sapphire', 'Saucy', 'Sea', 'Shadow', 'Sharp', 'Sheriff', 'Shining', 'Short', 'Shy', 'Silken', 'Silver', 'Simply', 'Smart', 'Smooth', 'Snappy', 'Sneaky', 'Soft', 'Sonic the', 'Southern', 'Speedy', 'Spicy', 'Starry', 'Stella', 'Stellar', 'Stinkin\'', 'Strict', 'Strong', 'Sugar', 'Sunny', 'Supercharged', 'Sus', 'Sweet', 'Tasty', 'The Great and Powerful', 'The Headless', 'The Real', 'Thunder', 'Timid', 'True True', 'Turquoise', 'Twilight', 'Under', 'Vanilla', 'Violet', 'Warning', 'Western', 'Windy', 'Whinny', 'Yellow', 'Zipp'];

  const names2 = ['Angel', 'Apple', 'Apples', 'Armor', 'Autumn', 'Bearded', 'Beauty', 'Bee', 'Beetle', 'Bell', 'Belle', 'Berries', 'Blade', 'Blink', 'Bloom', 'Bomb', 'Bon', 'Bones', 'Book', 'Boomer', 'Bread', 'Bridle', 'Brooch', 'Brook', 'Bubble', 'Bubbles', 'Bug', 'Bun', 'Bunny', 'Butterfly', 'Button', 'Cake', 'Candle', 'Cadance', 'Cat', 'Celestia', 'Chad', 'Charge', 'Cherry', 'Chungus', 'Cider', 'Class', 'Clearing', 'Cloud', 'Cola', 'Comet', 'Computer', 'Cook', 'Coomer', 'Crasher', 'Crate', 'Cream', 'Creeper', 'Crown', 'Crush', 'Crusher', 'Cupcakes', 'Curse', 'Daiquiri', 'Dance', 'Dash', 'Dashie', 'Day', 'Days', 'Deal', 'Derp', 'Dessert', 'Devil', 'Dew', 'Diamond', 'Do', 'Dog', 'Dovahkiin', 'Dream', 'Dress', 'Drop', 'Dubstep', 'Dust', 'Dusty', 'Earring', 'Eclair', 'Egg', 'Emerald', 'Envy', 'Eye', 'Eyes', 'Factory', 'Faith', 'Fall', 'Faust', 'Fear', 'Feather', 'Feathers', 'Fire', 'Flag', 'Flame', 'Flames', 'Flare', 'Floor', 'Flower', 'Flyer', 'Forest', 'Fork', 'Friend', 'Glass', 'Ground', 'Halo', 'Harvest', 'Hawk', 'Hayseed', 'Haze', 'Head', 'Heart', 'Hills', 'Hoof', 'Hooves', 'Hope', 'Horn', 'Horns', 'Horseman', 'Horseshoes', 'Hunter', 'Is Kill', 'Image', 'In Socks', 'Jack', 'Jazz', 'Journal', 'Kettle', 'Key', 'Knife', 'Knight', 'Ladle', 'Lady', 'Lake', 'Lamp', 'Leaf', 'Lemon', 'Lemonade', 'Light', 'List', 'Loaf', 'Love', 'Macintosh', 'Mane', 'Mi Amore Cadenza', 'Miner', 'Mint', 'Mist', 'Moon', 'Moonbow', 'Mouse', 'Muffin', 'Music', 'Necklace', 'Needle', 'News', 'Night', 'Noon', 'Note', 'Notes', 'Nova', 'Of Harmony', 'Patty', 'Petals', 'Pepper', 'Picnic', 'Picture', 'Pie', 'Pirate', 'Pixel', 'Poke', 'Pon3', 'Pony', 'Pride', 'Prism', 'Prod', 'Punk', 'Rainbow', 'Rat', 'Reaper', 'Rice', 'Ring', 'Rock', 'Roll', 'Romance', 'Rosette', 'Ruby', 'Runner', 'Saddle', 'Sapphire', 'Sebastian', 'Shield', 'Shimmer', 'Shine', 'Shower', 'Shy', 'Signal', 'Sky', 'Slim Shady', 'Snap', 'Snow', 'Song', 'Soul', 'Sparkle', 'Spawn', 'Sphere', 'Spider', 'Spin', 'Spirit', 'Spoon', 'Spring', 'Stairs', 'Star', 'Starscout', 'Stool', 'Storm', 'Strike', 'String', 'Stripe', 'Stuff', 'Summer', 'Sun', 'Swirl', 'Sword', 'Table', 'Tart', 'The Bard', 'The Changeling', 'The Damned', 'The Diamond Dog', 'The Eternal', 'The Grey', 'The Horse', 'The White', 'Tiara', 'Times', 'Top', 'Trailblazer', 'Tree', 'Trixie', 'Tron', 'Twilight', 'Twist', 'Virgin', 'Vise', 'Was Not The Imposter', 'Water', 'Waterfall', 'Wheat', 'Wind', 'Window', 'Wings', 'Winter', 'Wolf', 'Zoomer'];

  const names3 = ['5000 Candles On The Wind', 'An Actual Horse' ,'Angel', 'Apple', 'Applejack', 'Anonymous', 'Anonfilly', 'Autumn', 'Background Pony', 'Beardy', 'Beast', 'Bee', 'Beetlejuice', 'Berry', 'Black Knight', 'Blade', 'Blink', 'Blossom', 'Bomberman', 'Bon Bon', 'Bond. James Bond.', 'Bone Daddy', 'Book', 'Boomer', 'Boop', 'Bread', 'Brooch', 'Brook', 'Bubble', 'Bubbles', 'Bug', 'Bunny', 'Buttercup', 'Button', 'Cake', 'Candleja-', 'Cadance', 'Cat', 'Celestia', 'Chad Spike', 'Charge', 'Cherry', 'Chungus', 'Cider', 'Clearing', 'Cloud', 'Comet', 'Computer', 'Cook', 'Coomer', 'Crasher', 'Crate', 'Cream', 'Creeper', 'Crown', 'Crush', 'Crusher', 'Cupcake', 'Curse', 'Daiquiri', 'Dancer', 'Dash', 'Dashie', 'Deal Or No Deal', 'Derpy', 'Dessert', 'Deviled Eggs', 'Diamond', 'Diabeetus', 'Dovahkiin', 'Dress', 'Drop', 'Dubstep', 'Dusty', 'Earring', 'Eclair', 'Elmo', 'Egg', 'Eragon', 'Emerald', 'Envy', 'Eye', 'Exodia The Forbidden One', 'Factory', 'Faith', 'Fall', 'Fear', 'Feathers', 'Fire', 'Flame', 'Flare', 'Floor Bored', 'Flowers', 'Flutterguy', 'Flyer', 'Fool of a Took!', 'Forest Gump', 'Fork', 'Frost', 'Glass', 'Got Milk?', 'Ground', 'Halo', 'Harvest', 'Hawk', 'Hay Guys', 'Haze', 'Head', 'Heart', 'Help I\'m Trapped In A Username Making Factory', 'Hills', 'Hoof', 'Hooves Hooves', 'Hope', 'Horny Jail', 'Horseshoe', 'hunter2', 'iMacintosh', 'Imaginary Friend', 'IWTCIRD', 'Incognito', 'Jake From State Farm', 'Jazz', 'Joy', 'Kettle', 'Kimba the White Lion', 'Killer', 'Knoife', 'Ladle', 'Lady', 'Lake', 'Lamp', 'Leaf', 'Left Shark', 'Lemony Snicket', 'Lemonade', 'Lighthoof', 'Listen!', 'Loaf', 'Look At This Photograph', 'Love', 'Mane 6', 'Messiah', 'Mike Hunt', 'Minor', 'Minty', 'Misty', 'Moon', 'Moonpie', 'Mouse', 'Mtn Dew', 'Muffin', 'Neckbeard', 'Needledick', 'Newsflash', 'Night', 'Note:', 'Nova Prospekt', 'NukaCola', 'Numa Numa', 'Omellette Du Fromage', 'One Ring To Rule Them All', 'Pattycake', 'Peanut Butter Jelly Time', 'Petals', 'Pepper', 'Picnic', 'Pikachu', 'Pie', 'Pirate', 'Pixel', 'Pon3', 'Pony', 'Pride Rock', 'Prism', 'Punk', 'Rainbow', 'Rat', 'Rawr', 'Reaper', 'Rice', 'Rock', 'Roll', 'Romance', 'Rosetta Stoned', 'Ruby', 'Runner', 'Saddle', 'Saphira', 'Scruffy', 'Shielded', 'Shut', 'Shine', 'Shovel', 'Shower', 'Signal', 'Clear Skies a Cute', 'Slim Shady', 'Snapped', 'Snow', 'Soul', 'Sparkler', 'Spawnling', 'Spider-Man', 'Spin to Win', 'Spirit: Stallion of the Cimarron', 'Spoonman', 'Sproing', 'Stairs', 'Starkiller', 'Stool', 'Stormbreaker', 'Striker', 'Stringcheese', 'Stripes', 'Stuff', 'Summer', 'Sun', 'Sweep Sweep Sweep Sweep', 'Swirl', 'Sword', 'Table', 'Tard', 'The Cutie Mark Crusaders', 'The Fun Has Been Doubled!', 'The Imposter', 'The Sun Is A Deadly Lazer', 'The Old One', 'This is a sign.', 'Time', 'Todd Howard', 'Trailblazer', 'Treebeard', 'Trixie', 'Tron Legacy', 'Twiggie', 'Twist', 'Virginia', 'Vice City', 'Wake up Pickle', 'Waterga', 'Waterfall', 'Wheat', 'White Knight', 'Who Is This Four Chan', 'Whorse', 'Wind Waker', 'Windows 98', 'Winter', 'what the hay', 'Wolf In Sheep\s Clothing', 'Wojak', 'You Are Now Breathing Manually', 'You Lost The Game', 'Zoidberg', 'Zoinks'];


  const searchRegexp = new RegExp('\\b(Background Pony|Anonymous) #([0-9A-F])([0-9A-F])([0-9A-F])([0-9A-F])\\b');
  const styleCache = new Map();

  function getBPName(re, match) {
    const index1 = parseInt(match[2] + match[3],16);
    const index2 = parseInt(match[4] + match[5],16);
    const index3 = parseInt(match[2] + match[4],16);
    let ponyName = '';

    const title = match[2] + match[3] + match[4] + match[5];
    const className = (colored) ? getClassName(match[2] + match[3] + match[4] + match[5]) : '';

    if (index3 % 8 == 0) ponyName = names3[index1];
    else ponyName = names1[index1] + ' ' + names2[index2];

    const spanBefore = (fullNameColor) ? '' : ponyName + ' ';
    const spanInner = (fullNameColor) ? ponyName : '•';
    const spanAfter = (addBackgroundPony) ? ' the Background Pony' : '';

    const replacementString = `${spanBefore}<span title="${title}" class="${className}">${spanInner}</span>${spanAfter}`;

    re = re.replace(searchRegexp, replacementString);
    return re;
  }

  function processVanillaJSNode(node) {
    const nodeList = node.childNodes;

    for (let i = 0; i < nodeList.length; i++) {
      const childNode = nodeList[i];

      // text node found, do the replacement
      if (childNode.nodeType == 3) {
        const origString = childNode.data;
        const match = origString.match(searchRegexp);

        if (match) {
          childNode.data = getBPName(safe_tags_replace(origString), match);
          const skip = wrapTextNode(childNode);
          i += skip;
        }
      }
    }
  }

  function getClassName(id) {
    const className = `namize-bp--${id}`;

    if (!styleCache.has(className)) {
      const cssString = getColorStyle(id);
      styleCache.set(className, cssString);
      addStyle(`.${className} {${cssString}}`);
    }

    return className;
  }

  function getColorStyle(color16bit) {
    const pixel = parseInt(color16bit, 16);

    const red_mask = 0xF800;
    const green_mask = 0x7E0;
    const blue_mask = 0x1F;

    const red_value = (pixel & red_mask) >> 11;
    const green_value = (pixel & green_mask) >> 5;
    const blue_value = (pixel & blue_mask);

    // Expand to 8-bit values.
    let red = red_value << 3;
    let green = green_value << 2;
    let blue = blue_value << 3;

    if (darkTheme)
    {
        // Avoid very dark colored names
        if (red < 80) red = 80;
        if (green < 80) green = 80;
        if (blue < 80) blue = 80;
    }
    else
    {
        // Avoid very bright colored names
        if (red > 180) red = 180;
        if (green > 180) green = 180;
        if (blue > 180) blue = 180;
    }


    [red, green, blue] = [red, green, blue].map(val => val.toString().padStart(3));

    return `color: rgb(${red}, ${green}, ${blue});`;
  }

  function insertAfter(newEle, ele) {
    ele.parentNode.insertBefore(newEle, ele.nextSibling);
  }

  function wrapTextNode(textNode) {
    const div = document.createElement('div');
    div.innerHTML = textNode.textContent;

    const nodeCount = div.childNodes.length;
    let lastNode = textNode;
    let childNode = div.firstChild;

    while (childNode) {
      insertAfter(childNode, lastNode);
      lastNode = childNode;
      childNode = div.firstChild;
    }

    textNode.parentNode.removeChild(textNode);
    return nodeCount;
  }

  function replaceTag(tag) {
    const tagsToReplace = {
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;'
    };
    return tagsToReplace[tag] || tag;
  }

  function safe_tags_replace(str) {
    return str.replace(/[&<>]/g, replaceTag);
  }

  function addStyle(css) {
    const styleElement = document.getElementById(`${SCRIPT_ID}-style`);
    styleElement.innerHTML += css + '\n';
  }

  const styleElement = document.createElement('style');
  styleElement.setAttribute('type', 'text/css');
  styleElement.id = `${SCRIPT_ID}-style`;
  document.body.insertAdjacentElement('afterend', styleElement);

  const textNodes = document.evaluate( ".//a | .//strong | .//div[@class='metasection'] | .//div[contains(concat(' ', normalize-space(@class), ' '), ' post-author ')] | .//div[contains(concat(' ', normalize-space(@class), ' '), ' communication__body ')] | .//div[@class='small'] | .//div[@class='small-text'] | .//span[@class='block__header__title'] | .//td[@class='topic-lastpost'] | .//td[@class='table--communication-list__last-post'] | .//div[@id='duplicate_reporting']//td[@class='center image-cell'] | .//div[@class='block__content flex communication__options']/div[@class='flex__right'] | .//span[@class='spacing-right'] | .//main[@id='content']/h1 | .//div[@class='block__content alternating-color'] | .//div[@class='block__content flex alternating-color']/div[@class='flex__grow'] | .//td[@class='blank']",
    document.body,
    null,
    XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
    null);

  for (let i = 0; i < textNodes.snapshotLength; i++) {
    const node = textNodes.snapshotItem(i);
    processVanillaJSNode(node);
  }

  NodeCreationObserver.onCreation('div.comment_info > strong, div.comment_body a, div.post-prevue a, span.post-author, div.post-author, .communication__body__sender-name > strong, div.post-text a, div.communication__body__text a, .block__header__item', processVanillaJSNode);
})();