Skyrim Helper Source Code Viewer

See up my source code skirt! D=

..sadly, it does not do syntax highlighting for the htmls, javascripts, or csss D=
eh. oh well.


<?php

function text_to_class_mapper($input) {
    
// Replace spaces with dashes, remove single quotes, lowercase
    
return str_replace// replace spaces with dashes
        
" ",
        
'-',
        
str_replace// remove single quotes
            
"'",
            
'',
            
strtolower($input// lowercase
        
)
    );
}

function 
get_class_maps($input_to_translate$class_maps=array()) {
    
$translations = array();
    if ( !
is_array($input_to_translate) ) {
        
$input_to_translate = array($input_to_translate);
    }
    foreach ( 
$input_to_translate as $thing ) {
        if ( isset(
$class_maps[$thing]) ) {
            
$translations[] = $class_maps[$thing];
        }
    }
    return 
$translations;
}

function 
format_highlighter($effect_or_ingredient, &$class_maps) {
    if ( isset(
$class_maps[$effect_or_ingredient] ) ) {
        
$formatted text_to_class_mapper($effect_or_ingredient);
        return 
sprintf(
            
'<span class="highlight-%s">%s</span>',
            
$formatted,
            
$effect_or_ingredient
        
);
    }
    return 
$effect_or_ingredient;
}

function 
group_highlighter($effects_or_ingredients, &$class_maps) {
    return 
array_map
        function(
$ingredient) use ($class_maps) {
            return 
format_highlighter($ingredient$class_maps);
        },
        
$effects_or_ingredients
    
);
}

$json_data '{"Abecean Longfin":["Weakness to Frost","Fortify Sneak","Weakness to Poison","Fortify Restoration"],"Bear Claws":["Restore Stamina","Fortify Health","Fortify One-Handed","Damage Magicka Regen"],"Bee":["Restore Stamina","Ravage Stamina","Regenerate Stamina","Weakness to Shock"],"Beehive Husk":["Resist Poison","Fortify Light Armor","Fortify Sneak","Fortify Destruction"],"Bleeding Crown":["Weakness to Fire","Fortify Block","Weakness to Poison","Resist Magic"],"Blisterwort":["Damage Stamina","Restore Health","Frenzy","Fortify Smithing"],"Blue Butterfly Wing":["Damage Stamina","Damage Magicka Regen","Fortify Conjuration","Fortify Enchanting"],"Blue Dartwing":["Resist Shock","Restore Health","Fortify Pickpocket","Fear"],"Blue Mountain Flower":["Restore Health","Fortify Conjuration","Fortify Health","Damage Magicka Regen"],"Bone Meal":["Damage Stamina","Fortify Conjuration","Resist Fire","Ravage Stamina"],"Briar Heart":["Restore Magicka","Fortify Block","Paralysis","Fortify Magicka"],"Butterfly Wing":["Restore Health","Lingering Damage Stamina","Fortify Barter","Damage Magicka"],"Canis Root":["Damage Stamina","Fortify Marksman","Fortify One-Handed","Paralysis"],"Charred Skeever Hide":["Restore Stamina","Resist Poison","Cure Disease","Restore Health"],"Chaurus Eggs":["Weakness to Poison","Fortify Stamina","Damage Magicka","Invisibility"],"Chicken\'s Egg":["Resist Magic","Waterbreathing","Damage Magicka Regen","Lingering Damage Stamina"],"Creep Cluster":["Restore Magicka","Fortify Carry Weight","Damage Stamina Regen","Weakness to Magic"],"Crimson Nirnroot":["Damage Health","Invisibility","Damage Stamina","Resist Magic"],"Cyrodilic Spadetail":["Damage Stamina","Fear","Fortify Restoration","Ravage Health"],"Daedra Heart":["Damage Stamina Regen","Damage Magicka","Restore Health","Fear"],"Deathbell":["Damage Health","Ravage Stamina","Slow","Weakness to Poison"],"Dragon\'s Tongue":["Resist Fire","Fortify Barter","Fortify Illusion","Fortify Two-Handed"],"Dwarven Oil":["Weakness to Magic","Regenerate Magicka","Fortify Illusion","Restore Magicka"],"Ectoplasm":["Restore Magicka","Fortify Destruction","Fortify Magicka","Damage Health"],"Elves Ear":["Restore Magicka","Weakness to Frost","Fortify Marksman","Resist Fire"],"Eye of Sabre Cat":["Restore Stamina","Damage Magicka","Ravage Health","Restore Health"],"Falmer Ear":["Damage Health","Frenzy","Resist Poison","Fortify Lockpicking"],"Fire Salts":["Weakness to Frost","Restore Magicka","Resist Fire","Regenerate Magicka"],"Fly Amanita":["Resist Fire","Frenzy","Fortify Two-Handed","Regenerate Stamina"],"Frost Mirriam":["Resist Frost","Fortify Sneak","Ravage Magicka","Damage Stamina Regen"],"Frost Salts":["Weakness to Fire","Resist Frost","Restore Magicka","Fortify Conjuration"],"Garlic":["Resist Poison","Fortify Stamina","Regenerate Magicka","Regenerate Health"],"Giant Lichen":["Ravage Health","Weakness to Poison","Weakness to Shock","Restore Magicka"],"Giant\'s Toe":["Damage Stamina","Fortify Carry Weight","Fortify Health","Damage Stamina Regen"],"Glow Dust":["Damage Magicka","Fortify Destruction","Damage Magicka Regen","Resist Shock"],"Glowing Mushroom":["Resist Shock","Fortify Destruction","Fortify Smithing","Fortify Health"],"Grass Pod":["Resist Poison","Ravage Magicka","Fortify Alteration","Restore Magicka"],"Hagraven Claw":["Resist Magic","Lingering Damage Magicka","Fortify Enchanting","Fortify Barter"],"Hagraven Feathers":["Damage Magicka","Frenzy","Fortify Conjuration","Weakness to Shock"],"Hanging Moss":["Damage Magicka","Damage Magicka Regen","Fortify Health","Fortify One-Handed"],"Hawk Beak":["Restore Stamina","Resist Frost","Fortify Carry Weight","Resist Shock"],"Hawk Feathers":["Cure Disease","Fortify Light Armor","Fortify One-Handed","Fortify Sneak"],"Histcarp":["Restore Stamina","Fortify Magicka","Damage Stamina Regen","Waterbreathing"],"Honeycomb":["Restore Stamina","Fortify Block","Fortify Light Armor","Ravage Stamina"],"Human Flesh":["Damage Health","Paralysis","Restore Magicka","Fortify Sneak"],"Human Heart":["Damage Health","Damage Magicka Regen","Damage Magicka","Frenzy"],"Ice Wraith Teeth":["Weakness to Frost","Fortify Heavy Armor","Invisibility","Weakness to Fire"],"Imp Stool":["Damage Health","Paralysis","Lingering Damage Health","Restore Health"],"Jarrin Root":["Damage Health","Damage Stamina","Damage Magicka","Damage Magicka Regen"],"Jazbay Grapes":["Weakness to Magic","Fortify Magicka","Regenerate Magicka","Ravage Health"],"Juniper Berries":["Weakness to Fire","Regenerate Health","Fortify Marksman","Damage Stamina Regen"],"Large Antlers":["Restore Stamina","Fortify Stamina","Slow","Damage Stamina Regen"],"Lavender":["Resist Magic","Fortify Stamina","Ravage Magicka","Fortify Conjuration"],"Luna Moth Wing":["Damage Magicka","Fortify Light Armor","Regenerate Health","Invisibility"],"Moon Sugar":["Weakness to Fire","Resist Frost","Restore Magicka","Regenerate Magicka"],"Mora Tapinella":["Restore Magicka","Lingering Damage Health","Regenerate Stamina","Fortify Illusion"],"Mudcrab Chitin":["Restore Stamina","Cure Disease","Resist Poison","Resist Fire"],"Niamira\'s Rot":["Damage Magicka","Fear","Fortify Lockpicking","Regenerate Health"],"Nightshade":["Damage Health","Damage Magicka Regen","Lingering Damage Stamina","Fortify Destruction"],"Nirnroot":["Damage Health","Damage Stamina","Invisibility","Resist Magic"],"Nordic Barnacle":["Damage Magicka","Waterbreathing","Regenerate Health","Fortify Pickpocket"],"Orange Dartwing":["Restore Stamina","Ravage Magicka","Fortify Pickpocket","Lingering Damage Health"],"Pearl":["Restore Stamina","Restore Magicka","Fortify Block","Resist Shock"],"Pine Thrush Egg":["Restore Stamina","Fortify Lockpicking","Weakness to Poison","Resist Shock"],"Powdered Mammoth Tusk":["Restore Stamina","Weakness to Fire","Fortify Sneak","Fear"],"Purple Mountain Flower":["Restore Stamina","Fortify Sneak","Lingering Damage Magicka","Resist Frost"],"Red Mountain Flower":["Restore Magicka","Ravage Magicka","Fortify Magicka","Damage Health"],"River Betty":["Damage Health","Fortify Alteration","Slow","Fortify Carry Weight"],"Rock Warbler Egg":["Restore Health","Fortify One-Handed","Damage Stamina","Weakness to Magic"],"Sabre Cat Tooth":["Restore Stamina","Fortify Heavy Armor","Fortify Smithing","Weakness to Poison"],"Salt Pile":["Weakness to Magic","Fortify Restoration","Slow","Regenerate Magicka"],"Scaly Pholiata":["Weakness to Magic","Fortify Illusion","Regenerate Stamina","Fortify Carry Weight"],"Silverside Perch":["Restore Stamina","Damage Stamina Regen","Ravage Health","Resist Frost"],"Skeever Tail":["Damage Stamina Regen","Ravage Health","Damage Health","Fortify Light Armor"],"Slaughterfish Egg":["Resist Poison","Fortify Pickpocket","Lingering Damage Health","Fortify Stamina"],"Slaughterfish Scales":["Resist Frost","Lingering Damage Health","Fortify Heavy Armor","Fortify Block"],"Small Antlers":["Weakness to Poison","Fortify Restoration","Lingering Damage Stamina","Damage Health"],"Small Pearl":["Restore Stamina","Fortify One-Handed","Fortify Restoration","Resist Frost"],"Snowberries":["Resist Fire","Fortify Enchanting","Resist Frost","Resist Shock"],"Spider Egg":["Damage Stamina","Damage Magicka Regen","Fortify Lockpicking","Fortify Marksman"],"Spriggan Sap":["Damage Magicka Regen","Fortify Enchanting","Fortify Smithing","Fortify Alteration"],"Swamp Fungal Pod":["Resist Shock","Lingering Damage Magicka","Paralysis","Restore Health"],"Taproot":["Weakness to Magic","Fortify Illusion","Regenerate Magicka","Restore Magicka"],"Thistle Branch":["Resist Frost","Ravage Stamina","Resist Poison","Fortify Heavy Armor"],"Torchbug Thorax":["Restore Stamina","Lingering Damage Magicka","Weakness to Magic","Fortify Stamina"],"Troll Fat":["Resist Poison","Fortify Two-Handed","Frenzy","Damage Health"],"Tundra Cotton":["Resist Magic","Fortify Magicka","Fortify Block","Fortify Barter"],"Vampire Dust":["Invisibility","Regenerate Health","Restore Magicka","Cure Disease"],"Void Salts":["Weakness to Shock","Resist Magic","Damage Health","Fortify Magicka"],"Wheat":["Restore Health","Fortify Health","Damage Stamina Regen","Lingering Damage Magicka"],"White Cap":["Weakness to Frost","Fortify Heavy Armor","Restore Magicka","Ravage Magicka"],"Wisp Wrappings":["Restore Stamina","Fortify Destruction","Fortify Carry Weight","Resist Magic"]}';

$data_by_ingredient json_decode($json_data,1);

// I prefer my effects in alphabetic order for readability... but let's enable you to disable this...
if ( !isset($_GET['no_abc']) ) {
    
array_walk($data_by_ingredient, function(&$effects) { sort($effects); });
}

$data_by_effect = array();
$class_maps = array();

// Loop through ingredients/effects and build the class maps (for the JS side) and the data by effect grouping
foreach ( $data_by_ingredient as $ingredient => $effects ) {
    if ( !isset(
$class_maps[$ingredient]) ) {
        
$class_maps[$ingredient] = text_to_class_mapper($ingredient);
    }
    foreach ( 
$effects as $effect ) {
        if ( !isset(
$class_maps[$effect]) ) {
            
$class_maps[$effect] = text_to_class_mapper($effect);
        }
        if ( !isset(
$data_by_effect[$effect]) ) $data_by_effect[$effect] = array();
        
$data_by_effect[$effect][] = $ingredient;
    }
}

array_walk($data_by_effect, function($effects) { return array_unique($effects); });

ksort($data_by_ingredient);
ksort($data_by_effect);

?><!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Skyrim Alchemy Helper</title>
    <meta name="description" content="Helper to assist in all your Skyrim Alchemy Creations! See the effects by ingredient, ingredients by effects, AND both at the same time! Filter to your heart's content to create the best 3 part alchemical concoctions!</emphasis>">
    <style type="text/css">
    * { box-sizing: border-box; }
    html { width:100%; height:100%; font-size: 100%; }
    body { font-family: "Open Sans", sans-serif; background-color: #111; color: #888; }
    #content { margin: 2em 8em; }
    #fixed { position: fixed; width: 8em; left: 0px; top:5em; }
    #fixed ul { margin: 0; padding: 0; }
    #fixed li { list-style-type: none; text-indent: .5em }
    .bold {
        font-weight: bold;
    }
    .note {
        font-size: .8em;
        font-style: italic;
    }
    .highlight, .super-highlight {
        background-color: lightgrey;
        color: #1c1c1c;
    }
    span:hover {
        cursor: pointer;
    }
    .text-left { text-align: left; }
    .text-right { text-align: right; }
    .text-center { text-align: center; }
    .pull-left { float: left; }
    .pull-right { float: right; }
    #input-filter { width: 350px; }
    #status {
        display: inline-block;
        background-image: url("./includes/ajax-loader-mini.gif");
        background-position: center;
        background-repeat: no-repeat;
        width: 24px;
        height: 24px;
        margin-bottom: -6px;
    }
    .hide {
        display: none;
        visibility: hidden;
    }
    a {color: lightgrey;}
    a:hover {color: lightcoral;}
    a:focus {color:red;}
    a:active {color: white;}
    /*
        Best way to do table outlines! Thanks https://journalxtra.com/web-development/making-better-html-table-borders/ =]
    */
    table { background-color:#6d6d6d;border-spacing:1px; }
    td, th { background-color:#111; padding: 4px 8px; }
    </style>
</head>
<body>
    <div id="fixed">
        <ul>
            <li><a href="#ingredient">Ingredients</a></li>
            <li><a href="#effect">Effects</a></li>
            <li><a href="#all-the-things">All</a></li>
        </ul>
    </div>
    <div id="content">
        <h1 title="Hey - it ain't always pretty, but damn is it handy!">Skyrim Alchemy Helper</h1>
        <section class="pull-right">
            <h3>Usage</h3>
            <ul>
                <li>Find effects and/or ingredients with a comma separated list</li>
                <li>Hover over effects or ingredients to highlight similar</li>
                <li>Click to lock the highlighted item</li>
                <li>Double click to add item to the filter list</li>
                <li>Right click to hide an entire row (does not work in all section)</li>
            </ul>
            <h3>Buttons</h3>
            <ul>
                <li>Clear filter resets search & unhides all rows</li>
                <li>Clear filter does not remove highlights</li>
                <li>Unhide all rows to even show ones that do not match search</li>
                <li>Clear Highlights only removes all highlighted items</li>
            </ul>
        </section>
        <section>
            <input id="input-filter" type="text" placeholder="Type ingredient or effect to filter results" autofocus="autofocus"> <i id="status" class="hide"></i> <p style="display:inline;font-size:.8em;font-style:italic;">(filter will match on anything it can find)</p>
            <p style="margin-top:0px;">
                <button id="input-filter-reset-btn">Clear filter</button>
                <button id="clear-row-hide-btn">Unhide all Rows</button>
                <button id="highlighter-reset-btn">Clear Highlights</button>
            </p>
        </section>
        <section>
            <h2 id="ingredient">By Ingredient (effects in <?= !isset($_GET['no_abc']) ? 'alphabetical order <shhh class="note">[<a href="./alchemy.php?no_abc">swap</a>]</shhh>' 'original order <shhh class="note">[<a href="./alchemy.php">swap</a>]</shhh>' ?>)</h2>
            <table>
                <thead>
                    <tr class="text-left">
                        <th>Ingredient</th>
                        <th>Effect #1</th>
                        <th>Effect #2</th>
                        <th>Effect #3</th>
                        <th>Effect #4</th>
                    </tr>
                </thead>
                <tbody>
                    <? foreach ( $data_by_ingredient as $ingredient => $effects ) {
                    
printf(
                        
"<tr class='tr-data %s'>\n\t<td class='bold ingredient'>%s</td>\n\t<td>%s</td>\n\t<td>%s</td>\n\t<td>%s</td>\n\t<td>%s</td>\n</tr>\n",
                        
join(' 'array_merge( array(strtolower(text_to_class_mapper($ingredient))), get_class_maps($effects$class_maps) ) ),
                        
format_highlighter($ingredient$class_maps),
                        
format_highlighter($effects[0], $class_maps),
                        
format_highlighter($effects[1], $class_maps),
                        
format_highlighter($effects[2], $class_maps),
                        
format_highlighter($effects[3], $class_maps)
                    );
                } 
?>
                </tbody>
            </table>
        </section>
        <section>
            <h2 id="effect">By Effect</h2>
            <table>
                <thead>
                    <tr class="text-left">
                        <th>Effect</th>
                        <th>Ingredients</th>
                    </tr>
                </thead>
                <tbody>
                    <? foreach ( $data_by_effect as $effect => $list_of_ingredients ) {
                        
printf(
                            
"<tr class='tr-data %s'>\n\t<td class='bold effect'>%s</td>\n\t<td>%s</td>\n</tr>\n",
                            
join(' 'array_merge(array(strtolower(text_to_class_mapper($effect))), get_class_maps($list_of_ingredients$class_maps) ) ),
                            
format_highlighter($effect$class_maps),
                            
join(', 'group_highlighter($list_of_ingredients$class_maps))
                        );
                    } 
?>
                </tbody>
            </table>
        </section>
        <section>
            <h2 id="all-the-things" title="Think of this as a way to tie everything together">By All the Things (static reference)</h2>
            <table>
                <thead>
                    <tr class="text-left">
                        <th>Ingredient</th>
                        <th>Effect</th>
                        <th>Shared Ingredients</th>
                    </tr>
                </thead>
                <tbody>
                    <? foreach ( $data_by_ingredient as $ingredient => $effects ) {
                        
$first true// first time through, we need the ingredient in the row (rowspan=4)
                        
foreach ( $effects as $effect ) {
                            
printf(
                                
"%s\t<td>%s</td>\n\t<td>%s</td>\n</tr>\n",
                                
// I'm well aware I should be shot for writing code like this =P
                                
$first
                                    
sprintf// if it's the first pass, include ingredient
                                        
"<tr class='%s'>\n\t<th rowspan='4'>%s</th>\n",
                                        
join(
                                            
' ',
                                            
array_unique(
                                                
array_merge(
                                                    array(
                                                        
strtolower(text_to_class_mapper($ingredient))
                                                    ),
                                                    
get_class_maps($effects$class_maps),
                                                    
get_class_maps($data_by_effect[$effect], $class_maps)
                                                )
                                            )
                                        ),
                                        
format_highlighter($ingredient$class_maps)
                                    )
                                    : 
sprintf// otherwise, just start a new row
                                        
"<tr class='%s'>\n",
                                        
join(
                                            
' ',
                                            
array_unique(
                                                
array_merge(
                                                    array(
                                                        
strtolower(text_to_class_mapper($ingredient))
                                                    ),
                                                    
get_class_maps($effects$class_maps),
                                                    
get_class_maps($data_by_effect[$effect], $class_maps)
                                                )
                                            )
                                        )
                                    ),
                                
format_highlighter($effect$class_maps),
                                
join(', 'group_highlighter($data_by_effect[$effect], $class_maps))
                            );
                            if ( 
$first ) { // First time through the effects of the ingredient? Not anymore! Boom!
                                
$first false;
                            }
                        }
                    }
                    
?>
                </tbody>
            </table>
        </section>
        <hr>
        <p>Hey! If you thought this was handy or feel like there could be some fun improvements, <a href="mailto:ryan@rohjay.one">email me</a>!</p>
        <p class="note">...or just look at the <a href="./source.php?view=alchemy.php">source code for this page</a>.</p>
    </div>
<script type="text/javascript" src="./includes/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
    var allClassNames = <?= json_encode(array_values($class_maps)); ?>;
    var allRows = $('tr.tr-data');
    var statusThrobber = $('#status');
    var filterInput = $('#input-filter');
    var outerDate, stoppedAt, innerDate, currently, lastSearched;
    // Formatted text (like printf) graciously stolen from a response from user "malla" from here (slight fix w/argument):
    // https://coderwall.com/p/flonoa/simple-string-format-in-javascript
    String.prototype.format = function(arguments) {
        var a = this;
        for (var k in arguments) {
            a = a.replace(new RegExp("\\{" + k + "\\}", 'g'), arguments[k]);
        }
        return a;
    }
    function translateInputTextToClassFormat(userInput) {
        // replace spaces with dashes // remove single quotes // lowercase
        return userInput.map(function(strToFormat){
            return strToFormat.replace(/['"+]+/gi, '')
                .replace(/[^a-zA-Z0-9+]+/gi, '-')
                .toLowerCase();
        });
    }
    function findPotentialClassNames(translatedInputs, allClassNames) {
        var eachInputResults = translatedInputs.map(function(translatedInput){
            let potentialRegex = new RegExp(translatedInput+'.*', 'i');
            return allClassNames.filter(function(testClass) {
                return potentialRegex.test(testClass);
            });
        });
        // How to get an unique list in JS -- make it a set!
        // Lifted from https://stackoverflow.com/questions/10865025/merge-flatten-an-array-of-arrays-in-javascript
        // with love <3
        return new Set([].concat.apply([], eachInputResults));
    }
    function gatherEachName(inputString) {
        return inputString.split(',').map(function(splitString){ return splitString.trim(); }).filter(function(el){ return el != false; });
    }
    function resetData() {
        filterInput.val('');
        unhideRows();
    }
    function unhideRows() {
        allRows.removeClass('hide');
    }
    // Update the page when the user types
    filterInput.on('keyup change', function(e){
        e.preventDefault();
        var userInput = $(this).val();
        // prevents things like arrow keys from doing all of this all over again!
        if (userInput===lastSearched) {
            return;
        }
        statusThrobber.removeClass('hide');
        var names = gatherEachName(userInput);
        var translatedClassNames = translateInputTextToClassFormat(names);
        // Since I don't want to do *all this work every time* a keyup event happens...
        outerDate = new Date();
        stoppedAt = outerDate.getTime(); // last keyup marked at this time...
        setTimeout(function() {
            innerDate = new Date();
            currently = innerDate.getTime();
             // diff of the last keyup to the delayed run of this processing. Eh... pacman can entertain for a 1/4 sec =]
            var diff = currently - stoppedAt;
            if ( diff > 245 ) { // a little wiggle room (only asking for a 245ms delay rather than the 250ms timeout set)
                lastSearched = userInput;
                let potential_classes = findPotentialClassNames(translatedClassNames, allClassNames);
                var combinationDict = {};
                allRows.addClass('hide');
                potential_classes.forEach(function(className,k) {
                    let rows = $('.'+className);
                    rows.removeClass('hide');
                });
                statusThrobber.addClass('hide');
            }
        }, 250);
    });
    // Unhide the hidden rows (regardless of filter)
    $('#clear-row-hide-btn').on('click', function(e){
        e.preventDefault();
        unhideRows();
    });
    // Reset the page
    $('#input-filter-reset-btn').on('click', function(e){
        e.preventDefault();
        resetData();
    });
    // Highlighting effects
    $('span').on('mouseover', function(e){
        let originSpan = $(this);
        let originContent = originSpan.html();
        let highlightClass = this.className;
        let highlightedSpans = $('span.'+highlightClass);
        // As it turns out, adding/removing classes is faster than adjusting a css class's characteristics...
        // ...huh. Right?
        highlightedSpans.addClass('highlight');
        // Un-highlight when mouse outted
        originSpan.on('mouseout', function(e){
            highlightedSpans.removeClass('highlight');
        });
        // Super highlight if clicked
        originSpan.on('click', function(e){
            highlightedSpans.addClass('super-highlight');
            // Doesn't matter if it's the originSpan, if I click on one of these items, I want to turn
            // the highlighting off
            highlightedSpans.on('click', function(e){
                highlightedSpans.removeClass('super-highlight');
            });
        });
        // Double click to add to the filter
        originSpan.on('dblclick', function(e){
            let filterInputVal = $(filterInput).val();
            var currentValues = gatherEachName(filterInputVal);
            currentValues.push(originContent);
            filterInput.val(Array.from(new Set(currentValues)).join(', '));
            // Gotta kick a change event to run through the process
            $(filterInput).change();
            highlightedSpans.addClass('super-highlight');
            // Here too we'll un-highlight from any of the highlited spans
            highlightedSpans.on('click', function(e){
                highlightedSpans.removeClass('super-highlight');
            });
        });
    });
    // Ability to hide rows at a time using right-click
    // There is no .on('rightclick',function(){}) - so we have to be tricky!
    // isMousing will update to tell us when we're hovering over a row - if we're not hovering, go ahead and regular right click
    var isMousing = false;
    document.oncontextmenu = function() { return !isMousing; };
    $('tr.tr-data').on('mouseover', function(e){
        isMousing = true;
        let originRow = $(this);
        originRow.contextmenu(function(e){
            originRow.addClass('hide');
        });
        originRow.on('mouseout', function(e){
            isMousing = false;
        });
    });
    // Remove all the highlights
    $('#highlighter-reset-btn').on('click', function(e){
        e.preventDefault();
        // Shouldn't need to clear .highlight, but JIC I suppose in case something gets in a jenky state...?
        $('.highlight, .super-highlight').removeClass('highlight super-highlight');
    });
</script>
</body>
</html>

Didn't get enough? Go back to that fancy index page!

Hey! If you thought this was handy or feel like there could be some fun improvements, email me!