Jump to content

User:Firefly/more-block-info.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Firefly (talk | contribs) at 16:35, 21 December 2021 (++). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// ipaddr.js, MIT licensed
// see https://github.com/whitequark/ipaddr.js/blob/master/LICENSE for license and contributors
!(function(t) {
    !(function(t) {
        "use strict";
        const r = "(0?\\d+|0x[a-f0-9]+)",
            e = {
                fourOctet: new RegExp(`^${r}\\.${r}\\.${r}\\.${r}$`, "i"),
                threeOctet: new RegExp(`^${r}\\.${r}\\.${r}$`, "i"),
                twoOctet: new RegExp(`^${r}\\.${r}$`, "i"),
                longValue: new RegExp(`^${r}$`, "i"),
            },
            n = new RegExp("^0[0-7]+$", "i"),
            i = new RegExp("^0x[a-f0-9]+$", "i"),
            o = "(?:[0-9a-f]+::?)+",
            s = {
                zoneIndex: new RegExp("%[0-9a-z]{1,}", "i"),
                native: new RegExp(
                    `^(::)?(${o})?([0-9a-f]+)?(::)?(%[0-9a-z]{1,})?$`,
                    "i"
                ),
                deprecatedTransitional: new RegExp(
                    `^(?:::)(${r}\\.${r}\\.${r}\\.${r}(%[0-9a-z]{1,})?)$`,
                    "i"
                ),
                transitional: new RegExp(
                    `^((?:${o})|(?:::)(?:${o})?)${r}\\.${r}\\.${r}\\.${r}(%[0-9a-z]{1,})?$`,
                    "i"
                ),
            };

        function a(t, r) {
            if (t.indexOf("::") !== t.lastIndexOf("::")) return null;
            let e,
                n,
                i = 0,
                o = -1,
                a = (t.match(s.zoneIndex) || [])[0];
            for (
                a && ((a = a.substring(1)), (t = t.replace(/%.+$/, "")));
                (o = t.indexOf(":", o + 1)) >= 0;

            )
                i++;
            if (
                ("::" === t.substr(0, 2) && i--, "::" === t.substr(-2, 2) && i--, i > r)
            )
                return null;
            for (n = r - i, e = ":"; n--;) e += "0:";
            return (
                ":" === (t = t.replace("::", e))[0] && (t = t.slice(1)),
                ":" === t[t.length - 1] && (t = t.slice(0, -1)), {
                    parts: (r = (function() {
                        const r = t.split(":"),
                            e = [];
                        for (let t = 0; t < r.length; t++) e.push(parseInt(r[t], 16));
                        return e;
                    })()),
                    zoneId: a,
                }
            );
        }

        function p(t, r, e, n) {
            if (t.length !== r.length)
                throw new Error(
                    "ipaddr: cannot match CIDR for objects with different lengths"
                );
            let i,
                o = 0;
            for (; n > 0;) {
                if (((i = e - n) < 0 && (i = 0), t[o] >> i != r[o] >> i)) return !1;
                (n -= e), (o += 1);
            }
            return !0;
        }

        function u(t) {
            if (i.test(t)) return parseInt(t, 16);
            if ("0" === t[0] && !isNaN(parseInt(t[1], 10))) {
                if (n.test(t)) return parseInt(t, 8);
                throw new Error(`ipaddr: cannot parse ${t} as octal`);
            }
            return parseInt(t, 10);
        }

        function d(t, r) {
            for (; t.length < r;) t = `0${t}`;
            return t;
        }
        const c = {};
        (c.IPv4 = (function() {
            function t(t) {
                if (4 !== t.length)
                    throw new Error("ipaddr: ipv4 octet count should be 4");
                let r, e;
                for (r = 0; r < t.length; r++)
                    if (!(0 <= (e = t[r]) && e <= 255))
                        throw new Error("ipaddr: ipv4 octet should fit in 8 bits");
                this.octets = t;
            }
            return (
                (t.prototype.SpecialRanges = {
                    unspecified: [
                        [new t([0, 0, 0, 0]), 8]
                    ],
                    broadcast: [
                        [new t([255, 255, 255, 255]), 32]
                    ],
                    multicast: [
                        [new t([224, 0, 0, 0]), 4]
                    ],
                    linkLocal: [
                        [new t([169, 254, 0, 0]), 16]
                    ],
                    loopback: [
                        [new t([127, 0, 0, 0]), 8]
                    ],
                    carrierGradeNat: [
                        [new t([100, 64, 0, 0]), 10]
                    ],
                    private: [
                        [new t([10, 0, 0, 0]), 8],
                        [new t([172, 16, 0, 0]), 12],
                        [new t([192, 168, 0, 0]), 16],
                    ],
                    reserved: [
                        [new t([192, 0, 0, 0]), 24],
                        [new t([192, 0, 2, 0]), 24],
                        [new t([192, 88, 99, 0]), 24],
                        [new t([198, 51, 100, 0]), 24],
                        [new t([203, 0, 113, 0]), 24],
                        [new t([240, 0, 0, 0]), 4],
                    ],
                }),
                (t.prototype.kind = function() {
                    return "ipv4";
                }),
                (t.prototype.match = function(t, r) {
                    let e;
                    if (
                        (void 0 === r && ((t = (e = t)[0]), (r = e[1])),
                            "ipv4" !== t.kind())
                    )
                        throw new Error(
                            "ipaddr: cannot match ipv4 address with non-ipv4 one"
                        );
                    return p(this.octets, t.octets, 8, r);
                }),
                (t.prototype.prefixLengthFromSubnetMask = function() {
                    let t = 0,
                        r = !1;
                    const e = {
                        0: 8,
                        128: 7,
                        192: 6,
                        224: 5,
                        240: 4,
                        248: 3,
                        252: 2,
                        254: 1,
                        255: 0,
                    };
                    let n, i, o;
                    for (n = 3; n >= 0; n -= 1) {
                        if (!((i = this.octets[n]) in e)) return null;
                        if (((o = e[i]), r && 0 !== o)) return null;
                        8 !== o && (r = !0), (t += o);
                    }
                    return 32 - t;
                }),
                (t.prototype.range = function() {
                    return c.subnetMatch(this, this.SpecialRanges);
                }),
                (t.prototype.toByteArray = function() {
                    return this.octets.slice(0);
                }),
                (t.prototype.toIPv4MappedAddress = function() {
                    return c.IPv6.parse(`::ffff:${this.toString()}`);
                }),
                (t.prototype.toNormalizedString = function() {
                    return this.toString();
                }),
                (t.prototype.toString = function() {
                    return this.octets.join(".");
                }),
                t
            );
        })()),
        (c.IPv4.broadcastAddressFromCIDR = function(t) {
            try {
                const r = this.parseCIDR(t),
                    e = r[0].toByteArray(),
                    n = this.subnetMaskFromPrefixLength(r[1]).toByteArray(),
                    i = [];
                let o = 0;
                for (; o < 4;)
                    i.push(parseInt(e[o], 10) | (255 ^ parseInt(n[o], 10))), o++;
                return new this(i);
            } catch (t) {
                throw new Error("ipaddr: the address does not have IPv4 CIDR format");
            }
        }),
        (c.IPv4.isIPv4 = function(t) {
            return null !== this.parser(t);
        }),
        (c.IPv4.isValid = function(t) {
            try {
                return new this(this.parser(t)), !0;
            } catch (t) {
                return !1;
            }
        }),
        (c.IPv4.isValidFourPartDecimal = function(t) {
            return !(!c.IPv4.isValid(t) || !t.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/));
        }),
        (c.IPv4.networkAddressFromCIDR = function(t) {
            let r, e, n, i, o;
            try {
                for (
                    n = (r = this.parseCIDR(t))[0].toByteArray(),
                    o = this.subnetMaskFromPrefixLength(r[1]).toByteArray(),
                    i = [],
                    e = 0; e < 4;

                )
                    i.push(parseInt(n[e], 10) & parseInt(o[e], 10)), e++;
                return new this(i);
            } catch (t) {
                throw new Error("ipaddr: the address does not have IPv4 CIDR format");
            }
        }),
        (c.IPv4.parse = function(t) {
            const r = this.parser(t);
            if (null === r)
                throw new Error(
                    "ipaddr: string is not formatted like an IPv4 Address"
                );
            return new this(r);
        }),
        (c.IPv4.parseCIDR = function(t) {
            let r;
            if ((r = t.match(/^(.+)\/(\d+)$/))) {
                const t = parseInt(r[2]);
                if (t >= 0 && t <= 32) {
                    const e = [this.parse(r[1]), t];
                    return (
                        Object.defineProperty(e, "toString", {
                            value: function() {
                                return this.join("/");
                            },
                        }),
                        e
                    );
                }
            }
            throw new Error(
                "ipaddr: string is not formatted like an IPv4 CIDR range"
            );
        }),
        (c.IPv4.parser = function(t) {
            let r, n, i;
            if ((r = t.match(e.fourOctet)))
                return (function() {
                    const t = r.slice(1, 6),
                        e = [];
                    for (let r = 0; r < t.length; r++)(n = t[r]), e.push(u(n));
                    return e;
                })();
            if ((r = t.match(e.longValue))) {
                if ((i = u(r[1])) > 4294967295 || i < 0)
                    throw new Error("ipaddr: address outside defined range");
                return (function() {
                    const t = [];
                    let r;
                    for (r = 0; r <= 24; r += 8) t.push((i >> r) & 255);
                    return t;
                })().reverse();
            }
            return (r = t.match(e.twoOctet)) ?
                (function() {
                    const t = r.slice(1, 4),
                        e = [];
                    if ((i = u(t[1])) > 16777215 || i < 0)
                        throw new Error("ipaddr: address outside defined range");
                    return (
                        e.push(u(t[0])),
                        e.push((i >> 16) & 255),
                        e.push((i >> 8) & 255),
                        e.push(255 & i),
                        e
                    );
                })() :
                (r = t.match(e.threeOctet)) ?
                (function() {
                    const t = r.slice(1, 5),
                        e = [];
                    if ((i = u(t[2])) > 65535 || i < 0)
                        throw new Error("ipaddr: address outside defined range");
                    return (
                        e.push(u(t[0])),
                        e.push(u(t[1])),
                        e.push((i >> 8) & 255),
                        e.push(255 & i),
                        e
                    );
                })() :
                null;
        }),
        (c.IPv4.subnetMaskFromPrefixLength = function(t) {
            if ((t = parseInt(t)) < 0 || t > 32)
                throw new Error("ipaddr: invalid IPv4 prefix length");
            const r = [0, 0, 0, 0];
            let e = 0;
            const n = Math.floor(t / 8);
            for (; e < n;)(r[e] = 255), e++;
            return (
                n < 4 && (r[n] = (Math.pow(2, t % 8) - 1) << (8 - (t % 8))),
                new this(r)
            );
        }),
        (c.IPv6 = (function() {
            function t(t, r) {
                let e, n;
                if (16 === t.length)
                    for (this.parts = [], e = 0; e <= 14; e += 2)
                        this.parts.push((t[e] << 8) | t[e + 1]);
                else {
                    if (8 !== t.length)
                        throw new Error("ipaddr: ipv6 part count should be 8 or 16");
                    this.parts = t;
                }
                for (e = 0; e < this.parts.length; e++)
                    if (!(0 <= (n = this.parts[e]) && n <= 65535))
                        throw new Error("ipaddr: ipv6 part should fit in 16 bits");
                r && (this.zoneId = r);
            }
            return (
                (t.prototype.SpecialRanges = {
                    unspecified: [new t([0, 0, 0, 0, 0, 0, 0, 0]), 128],
                    linkLocal: [new t([65152, 0, 0, 0, 0, 0, 0, 0]), 10],
                    multicast: [new t([65280, 0, 0, 0, 0, 0, 0, 0]), 8],
                    loopback: [new t([0, 0, 0, 0, 0, 0, 0, 1]), 128],
                    uniqueLocal: [new t([64512, 0, 0, 0, 0, 0, 0, 0]), 7],
                    ipv4Mapped: [new t([0, 0, 0, 0, 0, 65535, 0, 0]), 96],
                    rfc6145: [new t([0, 0, 0, 0, 65535, 0, 0, 0]), 96],
                    rfc6052: [new t([100, 65435, 0, 0, 0, 0, 0, 0]), 96],
                    "6to4": [new t([8194, 0, 0, 0, 0, 0, 0, 0]), 16],
                    teredo: [new t([8193, 0, 0, 0, 0, 0, 0, 0]), 32],
                    reserved: [
                        [new t([8193, 3512, 0, 0, 0, 0, 0, 0]), 32]
                    ],
                }),
                (t.prototype.isIPv4MappedAddress = function() {
                    return "ipv4Mapped" === this.range();
                }),
                (t.prototype.kind = function() {
                    return "ipv6";
                }),
                (t.prototype.match = function(t, r) {
                    let e;
                    if (
                        (void 0 === r && ((t = (e = t)[0]), (r = e[1])),
                            "ipv6" !== t.kind())
                    )
                        throw new Error(
                            "ipaddr: cannot match ipv6 address with non-ipv6 one"
                        );
                    return p(this.parts, t.parts, 16, r);
                }),
                (t.prototype.prefixLengthFromSubnetMask = function() {
                    let t = 0,
                        r = !1;
                    const e = {
                        0: 16,
                        32768: 15,
                        49152: 14,
                        57344: 13,
                        61440: 12,
                        63488: 11,
                        64512: 10,
                        65024: 9,
                        65280: 8,
                        65408: 7,
                        65472: 6,
                        65504: 5,
                        65520: 4,
                        65528: 3,
                        65532: 2,
                        65534: 1,
                        65535: 0,
                    };
                    let n, i;
                    for (let o = 7; o >= 0; o -= 1) {
                        if (!((n = this.parts[o]) in e)) return null;
                        if (((i = e[n]), r && 0 !== i)) return null;
                        16 !== i && (r = !0), (t += i);
                    }
                    return 128 - t;
                }),
                (t.prototype.range = function() {
                    return c.subnetMatch(this, this.SpecialRanges);
                }),
                (t.prototype.toByteArray = function() {
                    let t;
                    const r = [],
                        e = this.parts;
                    for (let n = 0; n < e.length; n++)
                        (t = e[n]), r.push(t >> 8), r.push(255 & t);
                    return r;
                }),
                (t.prototype.toFixedLengthString = function() {
                    const t = function() {
                            const t = [];
                            for (let r = 0; r < this.parts.length; r++)
                                t.push(d(this.parts[r].toString(16), 4));
                            return t;
                        }
                        .call(this)
                        .join(":");
                    let r = "";
                    return this.zoneId && (r = `%${this.zoneId}`), t + r;
                }),
                (t.prototype.toIPv4Address = function() {
                    if (!this.isIPv4MappedAddress())
                        throw new Error(
                            "ipaddr: trying to convert a generic ipv6 address to ipv4"
                        );
                    const t = this.parts.slice(-2),
                        r = t[0],
                        e = t[1];
                    return new c.IPv4([r >> 8, 255 & r, e >> 8, 255 & e]);
                }),
                (t.prototype.toNormalizedString = function() {
                    const t = function() {
                            const t = [];
                            for (let r = 0; r < this.parts.length; r++)
                                t.push(this.parts[r].toString(16));
                            return t;
                        }
                        .call(this)
                        .join(":");
                    let r = "";
                    return this.zoneId && (r = `%${this.zoneId}`), t + r;
                }),
                (t.prototype.toRFC5952String = function() {
                    const t = /((^|:)(0(:|$)){2,})/g,
                        r = this.toNormalizedString();
                    let e,
                        n = 0,
                        i = -1;
                    for (;
                        (e = t.exec(r));)
                        e[0].length > i && ((n = e.index), (i = e[0].length));
                    return i < 0 ? r : `${r.substring(0, n)}::${r.substring(n + i)}`;
                }),
                (t.prototype.toString = function() {
                    return this.toNormalizedString().replace(/((^|:)(0(:|$))+)/, "::");
                }),
                t
            );
        })()),
        (c.IPv6.isIPv6 = function(t) {
            return null !== this.parser(t);
        }),
        (c.IPv6.isValid = function(t) {
            if ("string" == typeof t && -1 === t.indexOf(":")) return !1;
            try {
                const r = this.parser(t);
                return new this(r.parts, r.zoneId), !0;
            } catch (t) {
                return !1;
            }
        }),
        (c.IPv6.parse = function(t) {
            const r = this.parser(t);
            if (null === r.parts)
                throw new Error(
                    "ipaddr: string is not formatted like an IPv6 Address"
                );
            return new this(r.parts, r.zoneId);
        }),
        (c.IPv6.parseCIDR = function(t) {
            let r, e, n;
            if (
                (e = t.match(/^(.+)\/(\d+)$/)) &&
                (r = parseInt(e[2])) >= 0 &&
                r <= 128
            )
                return (
                    (n = [this.parse(e[1]), r]),
                    Object.defineProperty(n, "toString", {
                        value: function() {
                            return this.join("/");
                        },
                    }),
                    n
                );
            throw new Error(
                "ipaddr: string is not formatted like an IPv6 CIDR range"
            );
        }),
        (c.IPv6.parser = function(t) {
            let r, e, n, i, o, p;
            if ((n = t.match(s.deprecatedTransitional)))
                return this.parser(`::ffff:${n[1]}`);
            if (s.native.test(t)) return a(t, 8);
            if (
                (n = t.match(s.transitional)) &&
                ((p = n[6] || ""), (r = a(n[1].slice(0, -1) + p, 6)).parts)
            ) {
                for (
                    o = [
                        parseInt(n[2]),
                        parseInt(n[3]),
                        parseInt(n[4]),
                        parseInt(n[5]),
                    ],
                    e = 0; e < o.length; e++
                )
                    if (!(0 <= (i = o[e]) && i <= 255)) return null;
                return (
                    r.parts.push((o[0] << 8) | o[1]),
                    r.parts.push((o[2] << 8) | o[3]), { parts: r.parts, zoneId: r.zoneId }
                );
            }
            return null;
        }),
        (c.fromByteArray = function(t) {
            const r = t.length;
            if (4 === r) return new c.IPv4(t);
            if (16 === r) return new c.IPv6(t);
            throw new Error(
                "ipaddr: the binary input is neither an IPv6 nor IPv4 address"
            );
        }),
        (c.isValid = function(t) {
            return c.IPv6.isValid(t) || c.IPv4.isValid(t);
        }),
        (c.parse = function(t) {
            if (c.IPv6.isValid(t)) return c.IPv6.parse(t);
            if (c.IPv4.isValid(t)) return c.IPv4.parse(t);
            throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format");
        }),
        (c.parseCIDR = function(t) {
            try {
                return c.IPv6.parseCIDR(t);
            } catch (r) {
                try {
                    return c.IPv4.parseCIDR(t);
                } catch (t) {
                    throw new Error(
                        "ipaddr: the address has neither IPv6 nor IPv4 CIDR format"
                    );
                }
            }
        }),
        (c.process = function(t) {
            const r = this.parse(t);
            return "ipv6" === r.kind() && r.isIPv4MappedAddress() ?
                r.toIPv4Address() :
                r;
        }),
        (c.subnetMatch = function(t, r, e) {
            let n, i, o, s;
            for (i in ((void 0 !== e && null !== e) || (e = "unicast"), r))
                if (Object.prototype.hasOwnProperty.call(r, i))
                    for (!(o = r[i])[0] || o[0] instanceof Array || (o = [o]), n = 0; n < o.length; n++)
                        if (((s = o[n]), t.kind() === s[0].kind() && t.match.apply(t, s)))
                            return i;
            return e;
        }),
        "undefined" != typeof module && module.exports ?
            (module.exports = c) :
            (t.ipaddr = c);
    })(this);
})("undefined" == typeof window ? (window = {}) : window);
// end ipaddr.js

function mbi_logTimestampToWikiTimestamp(logTimestamp) {
    const logDatetime = new Date(logTimestamp);
    return (
        logDatetime.toLocaleString("en-GB", {
            hour: "2-digit",
            minute: "2-digit",
        }) +
        ", " +
        logDatetime.toLocaleString("en-GB", {
            day: "numeric",
            month: "long",
            year: "numeric",
        })
    );
}

function mbi_getBlockOptionsString(blockEntry) {
    let retVal = " (";
    if ("nocreate" in blockEntry) {
        // ACB
        retVal += "account creation blocked, ";
    }
    if (!("allowusertalk" in blockEntry)) {
        // NTP
        retVal += "cannot edit own talk page, ";
    }
    retVal += ")";
    retVal = retVal.replace(", )", ")").replace("()", "");
    return retVal;
}

function mbi_getBlockDisplayListItem(
    user,
    logid,
    timestamp,
    blockActor,
    blockComment,
    blockExpiry,
    blockOptions
) {
    return `<li data-mw-logaction="block/block" class="mw-logline-block" > <a href="/w/index.php?title=Special:Log&amp;logid=${logid}" title="Special:Log" >${timestamp}</a > <a href="/wiki/User:${blockActor}" class="mw-userlink userlink" title="User:${blockActor}" ><bdi>${blockActor}</bdi></a > <span class="mw-usertoollinks mw-changeslist-links" ><span ><a href="/wiki/User_talk:${blockActor}" class="mw-usertoollinks-talk userlink" title="User talk:${blockActor}" >talk</a ></span > <span ><a href="/wiki/Special:Contributions/${blockActor}" class="mw-usertoollinks-contribs userlink" title="Special:Contributions/${blockActor}" >contribs</a ></span ></span > blocked <a href="/wiki/Special:Contributions/${user}" class="mw-userlink mw-anonuserlink" title="Special:Contributions/${user}" ><bdi>${user}</bdi></a > <span class="mw-usertoollinks mw-changeslist-links" ><span ><a href="/w/index.php?title=User_talk:${user}&amp;action=edit&amp;redlink=1" class="new mw-usertoollinks-talk" title="User talk:${user}" >talk</a ></span ></span > with an expiration time of <span class="blockExpiry" title="${blockExpiry}">${blockExpiry}</span> ${blockOptions} <span class="comment">(${blockComment})</span > </li>`;
}

function mbi_getWarningboxLocked(
    username,
    logid,
    timestamp,
    lockActor,
    lockComment
) {
    return `<div class="warningbox mw-warning-with-logexcerpt mw-content-ltr more-block-info-box more-block-info-locked" dir="ltr" lang="en"> <p> This account is currently globally locked. The latest <span class="plainlinks" ><a class="external text" href="https://meta.wikimedia.org/wiki/Special:CentralAuth/${username}" >global account change log</a ></span > entry is provided below for reference: </p> <ul class="mw-logevent-loglines"> <li> <a href="https://meta.wikimedia.org/w/index.php?title=Special:Log&logid=${logid}" >${timestamp}</a > <a href="https://meta.wikimedia.org/wiki/User:${lockActor}" class="mw-userlink userlink" title="User:${lockActor}" ><bdi>${lockActor}</bdi></a > <span class="mw-usertoollinks mw-changeslist-links" ><span ><a href="https://meta.wikimedia.org/wiki/User_talk:${lockActor}" class="mw-usertoollinks-talk userlink" title="User talk:${lockActor}" >talk</a ></span > <span ><a href="https://meta.wikimedia.org/wiki/Special:Contributions/${lockActor}" class="mw-usertoollinks-contribs userlink" title="Special:Contributions/${lockActor}" >contribs</a ></span ></span > changed status for global account <a href="/wiki/User:${username}" class="mw-userlink userlink user-blocked-indef" ><bdi>${username}</bdi></a > <span class="mw-usertoollinks mw-changeslist-links" ><span ><a href="/wiki/User_talk:${username}" class="mw-usertoollinks-talk userlink user-blocked-indef" >talk</a ></span > <span ><a href="/wiki/Special:Contributions/${username}" class="mw-usertoollinks-contribs userlink user-blocked-indef" >contribs</a ></span ></span > : set locked; unset (none) <span class="comment">(${lockComment})</span> </li> </ul> <a href="https://meta.wikimedia.org/wiki/Special:CentralAuth/${username}">View full log</a> </div>`;
}

function mbi_HtmlEncode(rawString) {
    return $("<div>").text(rawString).html();
}

function mbi_insertMbiBox(boxContent) {
    let blockedNoticeElemList = $(".mw-contributions-blocked-notice");

    // If there's an existing block box, append after it
    // If not, just shove our box at the top of the content element
    if (blockedNoticeElemList.length > 0) {
        $($(".mw-contributions-blocked-notice")[0]).after(boxContent);
    } else {
        $("#mw-content-text").prepend(boxContent);
    }
}

async function mbi_getWarningboxRangeblocks(rangeblocks) {
    let liConcat = "";
    rangeblocks.forEach(function(blockEntry) {
        //const blockEntry = rangeblocks[cidr].blockEntries[0];
        const blockOptions = mbi_getBlockOptionsString(blockEntry);
        //const parsedComment = await mbi_parseWikitext(blockEntry.comment);
        liConcat += mbi_getBlockDisplayListItem(
            blockEntry.user.replace("User:", ""),
            blockEntry.id,
            mbi_logTimestampToWikiTimestamp(blockEntry.timestamp),
            blockEntry.by,
            mbi_HtmlEncode(blockEntry.reason), // HTML-encode the block summary as they often contain hidden notes
            mbi_logTimestampToWikiTimestamp(blockEntry.expiry),
            blockOptions
        );
    });
    return `<div class="warningbox mw-warning-with-logexcerpt mw-content-ltr more-block-info-box more-block-info-rangeblocks " dir="ltr" lang="en" > <p> This IP address is currently subject to one or more rangeblocks. A list of these rangeblocks is below: </p> <ul class="mw-logevent-loglines">${liConcat}</ul> </div>`;
}

// Shamelessly nicked from [[User:GeneralNotability/mark-locked.js]]
async function mbi_isLocked(user) {
    const api = new mw.Api();
    try {
        const response = await api.get({
            action: "query",
            list: "globalallusers",
            agulimit: "1",
            agufrom: user,
            aguto: user,
            aguprop: "lockinfo",
        });
        if (response.query.globalallusers.length === 0) {
            // If the length is 0, then we couldn't find the global user
            return false;
        }
        // If the 'locked' field is present, then the user is locked
        return "locked" in response.query.globalallusers[0];
    } catch (error) {
        return false;
    }
}

async function mbi_parseWikitext(wikitext, stripParaTag = true) {
    const api = new mw.Api();
    const apiResponse = await api.post({
        action: "parse",
        format: "json",
        text: wikitext,
        prop: "text",
        disableeditsection: 1,
        formatversion: "2",
    });
    const parsedText = apiResponse.parse.text;
    if (stripParaTag) {
        let div = document.createElement("div");
        div.innerHTML = parsedText;
        const pHtml = div.getElementsByTagName("p")[0].innerHTML;
        return pHtml.trim();
    } else {
        return parsedText.trim();
    }
}

async function mbi_getRangeblocks(user) {
    const retVal = {
        currentlyBlocked: false,
        everBlocked: false,
        blockEntries: [],
    };
    const api = new mw.Api();
    try {
        const response = await api.post({
            action: "query",
            list: "blocks",
            bkip: user,
        });

        let retVal = [];

        response.query.blocks.forEach(function(block) {
            if (block.user.contains("/")) {
                retVal.push(block);
            }
        });

        return retVal;
    } catch (error) {
        return undefined;
    }
}

/*async function mbi_getBlockEntries(user) {
    const now = Date.now();

    function blockIsCurrent(blockLogEntry) {
        return new Date(blockLogEntry.params.expiry) > now;
    }

    const retVal = {
        currentlyBlocked: false,
        everBlocked: false,
        blockEntries: [],
    };
    const api = new mw.Api();
    try {
        const response = await api.post({
            action: "query",
            list: "logevents",
            letitle: `User:${user}`,
            letype: "block",
        });
        const blockEntries = response.query.logevents;
        if (blockEntries.length > 0) {
            // user/range has been blocked at least once
            retVal.everBlocked = true;
            retVal.currentlyBlocked = blockIsCurrent(blockEntries[0]);
            retVal.blockEntries = blockEntries;
        }
        return retVal;
    } catch (error) {
        return undefined;
    }
}*/

async function mbi_getLockLogEntry(user) {
    const api = new mw.Api();
    try {
        const response = await $.post("https://meta.wikimedia.org/w/api.php", {
            action: "query",
            list: "logevents",
            lelimit: "1",
            letitle: `User:${user}@global`,
            leaction: "globalauth/setstatus",
            origin: "*",
            format: "json",
        });

        const logEvent = response.query.logevents[0];
        const wikiTimestamp = mbi_logTimestampToWikiTimestamp(logEvent.timestamp);
        const rawComment = logEvent.comment.replace("[[", "[[meta:"); // Crude fix for wikilinks in lock comments intending to point to Meta-Wiki pages
        const parsedComment = await mbi_parseWikitext(rawComment);
        const retVal = {
            logid: logEvent.logid,
            lockActor: logEvent.user,
            lockComment: parsedComment,
            timestamp: wikiTimestamp,
        };
        return retVal;
    } catch (error) {
        console.log(error);
        return undefined;
    }
}

$.when($.ready, mw.loader.using("mediawiki.util")).then(async function() {
    mw.util.addCSS(
        "div.more-block-info-box { " +
        "border: 1px solid #7196bc; " +
        "background-color: #dbedff; " +
        "}"
    );

    if (mw.config.get("wgPageName").includes("Special:Contributions/")) {
        const relevantUserName = mw.config.get("wgRelevantUserName");
        if (relevantUserName !== null) {
            // wgRelevantUserName is null for IP ranges (see phab T206954)
            const isip = mw.util.isIPAddress(relevantUserName, true);
            if (!isip) {
                // No point checking for locks if IP, IPs cannot be locked
                const userLocked = await mbi_isLocked(relevantUserName);
                if (userLocked) {
                    const lockLogEntry = await mbi_getLockLogEntry(relevantUserName);
                    const lockedBox = mbi_getWarningboxLocked(
                        relevantUserName,
                        lockLogEntry.logid,
                        lockLogEntry.timestamp,
                        lockLogEntry.lockActor,
                        lockLogEntry.lockComment
                    );
                    $("a[title='m:Global locks']").parent().remove(); // Remove "this account is globally locked" box
                    mbi_insertMbiBox(lockedBox); // Insert our info box
                }
            } else {
                // It's an IP, do rangeblock etc. checks
                const isipv6 = mw.util.isIPv6Address(relevantUserName, true);
                let rangeStart = 32;
                if (isipv6) {
                    range += 32; // Start at slash-64 for IPv6 (should this be 128...?)
                }

                /*function getNetworkAddress(addr) {
                                                                                                            if (isipv6) {
                                                                                                                //Hell knows at this point
                                                                                                            } else {
                                                                                                                return ipaddr.IPv4.networkAddressFromCIDR(addr).toString();
                                                                                                            }
                                                                                                        }*/

                const ipRangeBlocks = await mbi_getRangeblocks(relevantUserName);
                const rangeblockBox = await mbi_getWarningboxRangeblocks(ipRangeBlocks);
                mbi_insertMbiBox(rangeblockBox);
            }
        } else {
            // We're naively assuming an IP range
            console.log("IP RANGE YOU IDIOT");
        }
    }
});