// ==UserScript==
// @name Quick opcode filter for M&B mod wiki
// @description Add a small input box in the operations and triggers pages to easily look up, find and filter specific Mount&Blade 1.011 and Warband module system opcodes and triggers.
// @namespace https://gf.qytechs.cn/users/4813
//
// @match https://mbcommands.fandom.com/wiki/Operations
// @match https://antifandom.com/mbcommands/wiki/Operations
//
// @match https://mbcommands.fandom.com/wiki/Triggers
// @match https://antifandom.com/mbcommands/wiki/Triggers
//
// @icon https://static.wikia.nocookie.net/mount26blade20mooders20reference/images/4/4a/Site-favicon.ico/revision/latest
// @version 2025.07.21.3
// @author Swyter
// @license GNU GPLv3
// @grant none
// @run-at document-start
// ==/UserScript==
/* swy: append our new filter input box HTML element into the page */
search=document.createElement("input")
search.setAttribute("id", "opfilter")
search.setAttribute("type", "text")
search.setAttribute("spellcheck", "false")
search.setAttribute("placeholder", "Filter operations or triggers... :)")
document.documentElement.appendChild(search)
/* swy: append a new inline CSS stylesheet for our stuff into the page,
before it loads completely to avoid flicker */
style=document.createElement("style")
style.textContent = `
.operation[hidden],
.operation[hidden] + dl,
body[opfilter] .mw-parser-output p:not(.operation),
body[opfilter] .mw-parser-output pre,
body[opfilter] .mw-parser-output ol,
body[opfilter] .mw-parser-output ul,
body[opfilter] .mw-parser-output *:not(.operation) + dl,
body[opfilter] .mw-parser-output div,
body[opfilter] .mw-parser-output table,
aside.page__right-rail, /* swy: get rid of the useless right block cruft from Fandom; usually only shows ads */
div.main-page-box:has(a[href*=greasyfork]) /* swy: get rid of the tip mentioning this userscript if it's already installed */
{
display: none; /* swy: hide all the flowing text, explanations and tables while in filter mode */
}
input#opfilter
{
position: fixed;
width: calc(100% - (20% + 20%)); /* swy: center it leaving 20% of page width at either side */
left: calc(20%);
bottom: 20px;
box-shadow: 0 0 4px black, 0 0 40px black, 0 0 100px black;
opacity: .8;
z-index: 300; /* swy: make it appear when outside of the HTML <body>, the input element gets added to the HTML root */
font-size: x-large;
border: none;
border-radius: 2px;
padding: 3px;
}
input#opfilter:not(:focus)
{
opacity: .35; /* swy: fade it out when the input box is not focused */
}
* { scroll-margin-top: 100px; } /* swy: fix scrolling to an element but getting hidden by the top bar: https://stackoverflow.com/a/59253905/674685 */
`
document.documentElement.appendChild(style)
/* swy: on every new typed input do this */
search.oninput=function(e)
{
/* swy: hide all the non-operation stuff (text, explanations, ...) when using the search box; make it clean */
document.body.setAttribute("opfilter", "true")
/* swy: get a list of every operation, get the search text the user typed, split into words */
operations = document.querySelectorAll(".operation");
search_text = e.target.value
search_elems = search_text.toLowerCase().split(/\s+/)
/* swy: go across every operation in the page and show it, if it contains
*every* word in the filter, or just hide it otherwise */
for (var op of operations)
{
matches_all = true
for (var el of search_elems)
if (!op.id.includes(el))
matches_all = false
if (matches_all)
op.removeAttribute("hidden")
else
op.setAttribute("hidden", "true")
}
/* swy: hide empty headers (with no operation block hanging from it) */
wiki_content = document.querySelector(".mw-parser-output")
cur_elem = wiki_content.lastElementChild
header_regex = /H[0-9]/
parent_ctx = 0
ctx = 0
do
{
cur_elem_visible = !!cur_elem.offsetParent; /* swy: https://stackoverflow.com/a/21696585/674685 */
//cur_elem_visible && console.log("loop", cur_elem, cur_elem_visible, "parent_ctx", parent_ctx, "ctx", ctx)
if (cur_elem.nodeName.match(header_regex))
{
h_number = +cur_elem.nodeName[1] /* swy: 'h3' -> 3 */
//console.log("header", cur_elem, cur_elem.textContent, "parent_ctx", parent_ctx, "ctx", ctx, "h_number", h_number)
if ((ctx > 0 && h_number > 2) || (parent_ctx > 0 && h_number == 2))
cur_elem.removeAttribute("hidden")
else
cur_elem.setAttribute("hidden", "true")
if (h_number == 2)
parent_ctx = 0
ctx = 0
//console.log("header", cur_elem, cur_elem.textContent, "parent_ctx", parent_ctx, "ctx", ctx, "h_number", h_number)
}
else if(cur_elem_visible)
{
ctx++
parent_ctx++
//console.log("other", cur_elem, ctx)
}
} while (cur_elem = cur_elem.previousElementSibling)
/* swy: scroll into the first (visible) operation that matches our search, if any */
if (first_visible_op = document.querySelector('.operation:not([hidden])'))
first_visible_op.scrollIntoView();
}