<!-- phpinfo() search --> <form style="position: sticky; top: 0;"> <input> </form> <script defer>// /* * phpinfo() search javascript (~ES5/6) */ const w = window; const d = document; const q = d.querySelector.bind(d); const query = function* (selector, element) { yield* (element || d).querySelectorAll(selector); }; const walk = function* (next, start) { for (let look = next(start); look !== null; look = next(look)) yield look; }; const filter = function* (condition, iter) { for (const item of iter) if (condition(item)) yield item; }; const aslong = function* (condition, iter) { for (const item of iter) { if (!condition(item)) break; yield item; } }; const on = function(probe, condition, result) { if (probe !== null && condition(probe) && result) return result(probe); return null; }; const first = function(condition, iter) { for (item of iter) if (condition === null || condition(item)) return item; return null; }; const has = function (condition, iter) { for (item of filter(condition, iter)) return true; return false; }; const name = e => e.tagName; const height = e => e.clientHeight; const visible = e => !e.hidden; const parent = e => e.parentElement; const forward = e => e.nextElementSibling; const backward = e => e.previousElementSibling; const string = function (e, otherwise) { return ("string" === typeof e) ? e : otherwise(e); }; const isNot = function(func) { return e => !func(e); }; const isString = function(str, map) { str = string(str, map); return e => map(e) === str; }; const is = e => !!e; const isName = e => isString(e, name); const notName = e => isNot(isName(e)); const reduce = function (aggregate, func, iter) { for (const item of iter) aggregate = func(aggregate, item); return aggregate; }; const map = function* (func, iter) { for (const item of iter) yield func(item); }; const sum = function(iter) { return reduce(0, (a, e) => a + e, iter); }; const look = function (next, start) { return first(notName(start), walk(next, start)); }; const event = function(e, type, listener, extra) { e.addEventListener(type, listener, extra); }; const sections = new Set(); sections.empty = true; const search_terms = function (terms) { const regExp = new RegExp(terms.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i"); const tables = new WeakSet(); tables.has = tables.has.bind(tables); for (const e of query("table tr td.e, table tr:not(.h) td:not([class])")) { let row = parent(e); row.hidden = ! regExp.test(row.innerText); tables.add(parent(parent(row))); }; for (const table of query("table")) { table.hidden = false; table.hidden = terms.length && ( height(table) < 2 || ( table.rows[0].className === "h" && height(table.tBodies[0]) === sum(map(height, query('tr.h', table))) ) ); sections.empty && on(look(backward, table), isName('H2'), h2 => sections.add(h2)) } sections.empty = false; sections.forEach(h2 => h2.hidden = !has(visible, aslong(isName('TABLE'), walk(forward, h2)))); history.replaceState({}, "search for " + terms, "#" + encodeURIComponent(terms)); }; const input = q("form input"); let c = 0; const search = function(terms) { c++; w.setTimeout(function() { if (c === 0) {return;} c = 0; const untie = ("function" === typeof terms) ? terms() : terms; input.value = untie; search_terms(untie); }, 100); }; event(input, "input", function() { search(function() {return this.value;}.bind(this)); }, false); const hash = function() { const string = w.location.hash; search(decodeURIComponent(string).replace(/^#/, '')); }; event(w, "hashchange", hash, false); event(d, "DOMContentLoaded", hash, false); //</script>