<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://linguifex.com/w/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3AGadget-AutoContrastFixer.js</id>
	<title>MediaWiki:Gadget-AutoContrastFixer.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://linguifex.com/w/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3AGadget-AutoContrastFixer.js"/>
	<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=MediaWiki:Gadget-AutoContrastFixer.js&amp;action=history"/>
	<updated>2026-04-07T04:06:56Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://linguifex.com/w/index.php?title=MediaWiki:Gadget-AutoContrastFixer.js&amp;diff=474839&amp;oldid=prev</id>
		<title>Sware: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=MediaWiki:Gadget-AutoContrastFixer.js&amp;diff=474839&amp;oldid=prev"/>
		<updated>2025-11-04T17:46:54Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 17:46, 4 November 2025&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Sware</name></author>
	</entry>
	<entry>
		<id>https://linguifex.com/w/index.php?title=MediaWiki:Gadget-AutoContrastFixer.js&amp;diff=474838&amp;oldid=prev</id>
		<title>wikt&gt;Surjection at 16:06, 8 October 2025</title>
		<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=MediaWiki:Gadget-AutoContrastFixer.js&amp;diff=474838&amp;oldid=prev"/>
		<updated>2025-10-08T16:06:59Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;// &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/* jshint maxerr:1048576, strict:true, undef:true, latedef:true, esversion:6 */&lt;br /&gt;
/* global $, mw */&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Automatically adjusts contrast of templates in night mode.&lt;br /&gt;
 *&lt;br /&gt;
 * Author(s): Surjection&lt;br /&gt;
 * Last updated: 2025-10-08&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
// [[MediaWiki:Gadget-WiktGadgetPrefs.js]]&lt;br /&gt;
const preferences = mw.wiktGadgetPrefs.get(&lt;br /&gt;
    &amp;quot;autoContrastFixer&amp;quot;,&lt;br /&gt;
    {&lt;br /&gt;
        label: {&lt;br /&gt;
            en: &amp;quot;Automatically fix custom template colors in night mode&amp;quot;,&lt;br /&gt;
        },&lt;br /&gt;
    },&lt;br /&gt;
    {&lt;br /&gt;
        darkTextColorOverride: {&lt;br /&gt;
            type: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
            default: false,&lt;br /&gt;
            label: {&lt;br /&gt;
                en: &amp;quot;Apply also to templates with explicit dark text colors&amp;quot;,&lt;br /&gt;
            },&lt;br /&gt;
        },&lt;br /&gt;
    }&lt;br /&gt;
);&lt;br /&gt;
const darkTextColorOverride = preferences.darkTextColorOverride;&lt;br /&gt;
&lt;br /&gt;
/* Color conversion code. */&lt;br /&gt;
/**************************/&lt;br /&gt;
&lt;br /&gt;
function parseCssValue(cssAlpha, divisor) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Parses a numeric value in CSS syntax, either given directly or&lt;br /&gt;
     * as a percentage.&lt;br /&gt;
     *&lt;br /&gt;
     * If a divisor is given, it is used to scale the value, unless it is&lt;br /&gt;
     * a percentage.&lt;br /&gt;
     */&lt;br /&gt;
    if (cssAlpha.endsWith(&amp;quot;%&amp;quot;)) {&lt;br /&gt;
        return parseCssValue(cssAlpha.slice(0, -1).trim(), 100.0);&lt;br /&gt;
    }&lt;br /&gt;
    return Number(cssAlpha) / (divisor || 1.0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// RegEx used by parseCssColor&lt;br /&gt;
const RE_CSS_HEX_COLOR_3 = /^#([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])$/;&lt;br /&gt;
const RE_CSS_HEX_COLOR_6 =&lt;br /&gt;
    /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/;&lt;br /&gt;
const RE_CSS_RGB_COLOR =&lt;br /&gt;
    /^rgb\(\s*([0-9.]+\s*%?)(?:\s+|\s*,\s*)([0-9.]+\s*%?)(?:\s+|\s*,\s*)([0-9.]+\s*%?)\s*\)$/;&lt;br /&gt;
const RE_CSS_RGBA_COLOR =&lt;br /&gt;
    /^rgba?\(\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*,\s*([0-9.]+\s*%?)\s*\)$/;&lt;br /&gt;
const RE_CSS_RGBA_SLASH_COLOR =&lt;br /&gt;
    /^rgba?\(\s*([0-9.]+\s*%?)\s+([0-9.]+\s*%?)\s+([0-9.]+\s*%?)\s*\/\s*([0-9.]+\s*%?)\s*\)$/;&lt;br /&gt;
&lt;br /&gt;
function parseCssColor(cssColor) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Parses a color in CSS syntax and returns it in RGBA format:&lt;br /&gt;
     *&lt;br /&gt;
     * [r, g, b, a]&lt;br /&gt;
     *      with each value within [0, 1].&lt;br /&gt;
     *&lt;br /&gt;
     * Returns undefined if the color could not be parsed.&lt;br /&gt;
     */&lt;br /&gt;
    let m;&lt;br /&gt;
    if ((m = cssColor.match(RE_CSS_RGB_COLOR))) {&lt;br /&gt;
        return [&lt;br /&gt;
            parseCssValue(m[1], 255.0),&lt;br /&gt;
            parseCssValue(m[2], 255.0),&lt;br /&gt;
            parseCssValue(m[3], 255.0),&lt;br /&gt;
            1.0,&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
    if ((m = cssColor.match(RE_CSS_RGBA_COLOR))) {&lt;br /&gt;
        return [&lt;br /&gt;
            parseCssValue(m[1], 255.0),&lt;br /&gt;
            parseCssValue(m[2], 255.0),&lt;br /&gt;
            parseCssValue(m[3], 255.0),&lt;br /&gt;
            parseCssValue(m[4]),&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
    if ((m = cssColor.match(RE_CSS_RGBA_SLASH_COLOR))) {&lt;br /&gt;
        return [&lt;br /&gt;
            parseCssValue(m[1], 255.0),&lt;br /&gt;
            parseCssValue(m[2], 255.0),&lt;br /&gt;
            parseCssValue(m[3], 255.0),&lt;br /&gt;
            parseCssValue(m[4]),&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
    if ((m = cssColor.match(RE_CSS_HEX_COLOR_6))) {&lt;br /&gt;
        return [&lt;br /&gt;
            parseInt(m[1], 16) / 255.0,&lt;br /&gt;
            parseInt(m[2], 16) / 255.0,&lt;br /&gt;
            parseInt(m[3], 16) / 255.0,&lt;br /&gt;
            1.0,&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
    if ((m = cssColor.match(RE_CSS_HEX_COLOR_3))) {&lt;br /&gt;
        return [&lt;br /&gt;
            parseInt(m[1], 16) / 15.0,&lt;br /&gt;
            parseInt(m[2], 16) / 15.0,&lt;br /&gt;
            parseInt(m[3], 16) / 15.0,&lt;br /&gt;
            1.0,&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
    if (cssColor === &amp;quot;transparent&amp;quot;) return [0, 0, 0, 0];&lt;br /&gt;
    return undefined;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function makeCssColor(rgbaColor) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Converts the given RGBA color into CSS format.&lt;br /&gt;
     */&lt;br /&gt;
    const [r, g, b, a] = rgbaColor;&lt;br /&gt;
    const normR = Math.round(r * 255.0);&lt;br /&gt;
    const normG = Math.round(g * 255.0);&lt;br /&gt;
    const normB = Math.round(b * 255.0);&lt;br /&gt;
    if (a &amp;gt;= 1.0) {&lt;br /&gt;
        return `rgb(${normR}, ${normG}, ${normB})`;&lt;br /&gt;
    } else {&lt;br /&gt;
        return `rgba(${normR}, ${normG}, ${normB}, ${a})`;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function rgbToHsl(r, g, b) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Converts colors from RGB to HSL (hue, saturation, lightness).&lt;br /&gt;
     *&lt;br /&gt;
     * The RGB inputs should all lie within [0, 1].&lt;br /&gt;
     *&lt;br /&gt;
     * With valid inputs, the hue value will lie within [0, 6],&lt;br /&gt;
     * while the saturation and lightness values will lie within [0, 1].&lt;br /&gt;
     */&lt;br /&gt;
    const min = Math.min(r, g, b);&lt;br /&gt;
    const max = Math.max(r, g, b);&lt;br /&gt;
    const delta = max - min;&lt;br /&gt;
    const l = (max + min) / 2;&lt;br /&gt;
&lt;br /&gt;
    if (delta === 0) {&lt;br /&gt;
        return [0, 0, l];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const s = l &amp;gt; 0.5 ? delta / (2 - max - min) : delta / (max + min);&lt;br /&gt;
&lt;br /&gt;
    let h;&lt;br /&gt;
    if (max === r) h = (g - b) / delta + (g &amp;lt; b ? 6 : 0);&lt;br /&gt;
    else if (max === g) h = (b - r) / delta + 2;&lt;br /&gt;
    else if (max === b) h = (r - g) / delta + 4;&lt;br /&gt;
    return [h, s, l];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function hslToRgb(h, s, l) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Converts colors from HSL (hue, saturation, lightness) to RGB.&lt;br /&gt;
     *&lt;br /&gt;
     * The hue value should lie within [0, 6], and the saturation and&lt;br /&gt;
     * lightness values within [0, 1].&lt;br /&gt;
     *&lt;br /&gt;
     * With valid inputs, the RGB outputs will all lie within [0, 1].&lt;br /&gt;
     */&lt;br /&gt;
    if (s === 0) {&lt;br /&gt;
        return [l, l, l];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const q = l &amp;lt; 0.5 ? l * (1 + s) : l + s - l * s;&lt;br /&gt;
    const p = 2 * l - q;&lt;br /&gt;
    h /= 6;&lt;br /&gt;
    if (h &amp;lt; 0) h = 1 - h;&lt;br /&gt;
    h %= 1;&lt;br /&gt;
&lt;br /&gt;
    function hueToRgb(p, q, t) {&lt;br /&gt;
        if (t &amp;lt; 0) t += 1;&lt;br /&gt;
        else if (t &amp;gt; 1) t -= 1;&lt;br /&gt;
&lt;br /&gt;
        if (t &amp;lt; 1 / 6) return p + (q - p) * 6 * t;&lt;br /&gt;
        if (t &amp;lt; 1 / 2) return q;&lt;br /&gt;
        if (t &amp;lt; 2 / 3) return p + (q - p) * 6 * (2 / 3 - t);&lt;br /&gt;
        return p;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const r = hueToRgb(p, q, h + 1 / 3);&lt;br /&gt;
    const g = hueToRgb(p, q, h);&lt;br /&gt;
    const b = hueToRgb(p, q, h - 1 / 3);&lt;br /&gt;
    return [r, g, b];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Color processing code. */&lt;br /&gt;
/**************************/&lt;br /&gt;
&lt;br /&gt;
function colorLuminance(rgbaColor) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the luminance of the RGB color (alpha is ignored) using the&lt;br /&gt;
     * sRGB/BT.709 luminance formula.&lt;br /&gt;
     */&lt;br /&gt;
    const [r, g, b] = rgbaColor;&lt;br /&gt;
    const convertSrgbChannel = (x) =&amp;gt; x &amp;lt;= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);&lt;br /&gt;
    return 0.2126 * convertSrgbChannel(r) + 0.7152 * convertSrgbChannel(g) + 0.0722 * convertSrgbChannel(b);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function colorContrast(fgRgbaColor, bgRgbaColor) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the WCAG contrast ratio (&amp;gt;= 1) for the given two colors.&lt;br /&gt;
     */&lt;br /&gt;
    const q =&lt;br /&gt;
        (colorLuminance(fgRgbaColor) + 0.05) /&lt;br /&gt;
        (colorLuminance(bgRgbaColor) + 0.05);&lt;br /&gt;
    return q &amp;lt; 1 ? 1 / q : q;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function darkenBackgroundColor(rgbaColor) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Darkens the given RGBA color for it to make it a suitable&lt;br /&gt;
     * background color in night mode.&lt;br /&gt;
     */&lt;br /&gt;
    const [r, g, b, a] = rgbaColor;&lt;br /&gt;
    const [h, s, l] = rgbToHsl(r, g, b);&lt;br /&gt;
&lt;br /&gt;
    let lAdj = 1 - colorLuminance(rgbaColor);&lt;br /&gt;
    lAdj = 0.02 + 0.98 * Math.pow(lAdj, 0.95);&lt;br /&gt;
&lt;br /&gt;
    const lScale = Math.min(Math.max(lAdj, 0.01) / Math.max(l, 0.01), 1);&lt;br /&gt;
    const [rr, gg, bb] = hslToRgb(h, (s + s * lScale) / 2, lAdj);&lt;br /&gt;
    return [rr, gg, bb, a];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function darkenBorderColor(rgbaColor) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Darkens the given RGBA color for it to make it a suitable&lt;br /&gt;
     * border color in night mode.&lt;br /&gt;
     */&lt;br /&gt;
    const [r, g, b, a] = rgbaColor;&lt;br /&gt;
    const [h, s, l] = rgbToHsl(r, g, b);&lt;br /&gt;
    const [rr, gg, bb] = hslToRgb(h, s * 0.5, l * 0.5);&lt;br /&gt;
    return [rr, gg, bb, a];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function lightenForegroundColor(rgbaColor) {&lt;br /&gt;
    /**&lt;br /&gt;
     * Lightens the given RGBA color for it to make it a suitable&lt;br /&gt;
     * text color in night mode.&lt;br /&gt;
     */&lt;br /&gt;
    const [r, g, b, a] = rgbaColor;&lt;br /&gt;
    const [h, s, l] = rgbToHsl(r, g, b);&lt;br /&gt;
    const [rr, gg, bb] = hslToRgb(h, s * 0.5, 0.5 + (1 - l) * 0.5);&lt;br /&gt;
    return [rr, gg, bb, a];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Auto-fixer parameters. */&lt;br /&gt;
/**************************/&lt;br /&gt;
&lt;br /&gt;
/* Whether to adjust border colors. */&lt;br /&gt;
const PARAMETER_ADJUST_BORDER_COLORS = false;&lt;br /&gt;
/** Any background-foreground combo with the contrast ratio exceeding&lt;br /&gt;
 * this value are ignored by the fixer. */&lt;br /&gt;
const PARAMETER_MAXIMUM_CONTRAST_TO_FIX = 3;&lt;br /&gt;
/** The background color must have at least this much alpha,&lt;br /&gt;
 * or the element is ignored by the fixer. */&lt;br /&gt;
const PARAMETER_MINIMUM_BG_ALPHA = 0.5;&lt;br /&gt;
/** The foreground color must have at least this much luminance,&lt;br /&gt;
 * or the element is ignored by the fixer. */&lt;br /&gt;
const PARAMETER_MINIMUM_FG_LUMA = 0.5;&lt;br /&gt;
/** The border color must have at least this much luminance,&lt;br /&gt;
 * or it is ignored by the fixer. */&lt;br /&gt;
const PARAMETER_MINIMUM_BORDER_LUMA = 0.7;&lt;br /&gt;
&lt;br /&gt;
/* Auto-fixer framework. */&lt;br /&gt;
/*************************/&lt;br /&gt;
&lt;br /&gt;
function isSkinNightMode() {&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns true if the user has requested night mode.&lt;br /&gt;
     */&lt;br /&gt;
    if (!window.matchMedia) return;&lt;br /&gt;
&lt;br /&gt;
    const htmlRoot = document.documentElement;&lt;br /&gt;
    const nightPref = htmlRoot.classList.contains(&lt;br /&gt;
        &amp;quot;skin-theme-clientpref-night&amp;quot;&lt;br /&gt;
    );&lt;br /&gt;
    const osPref = htmlRoot.classList.contains(&amp;quot;skin-theme-clientpref-os&amp;quot;);&lt;br /&gt;
    const isScreen = window.matchMedia(&amp;quot;screen&amp;quot;).matches;&lt;br /&gt;
    const prefersDark = window.matchMedia(&lt;br /&gt;
        &amp;quot;(prefers-color-scheme: dark)&amp;quot;&lt;br /&gt;
    ).matches;&lt;br /&gt;
&lt;br /&gt;
    return isScreen &amp;amp;&amp;amp; (nightPref || (osPref &amp;amp;&amp;amp; prefersDark));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function doAutoContrastFix() {&lt;br /&gt;
    /**&lt;br /&gt;
     * Applies the auto-contrast fix to all elements in the&lt;br /&gt;
     * wikipage body.&lt;br /&gt;
     */&lt;br /&gt;
    // see if the user has requested night mode.&lt;br /&gt;
    if (!isSkinNightMode()) return;&lt;br /&gt;
&lt;br /&gt;
    const view = document.defaultView;&lt;br /&gt;
    const ELEMENTS_PER_FRAME = 50;&lt;br /&gt;
&lt;br /&gt;
    function isCandidate(el) {&lt;br /&gt;
        const style = view.getComputedStyle(el);&lt;br /&gt;
        return style.background !== &amp;quot;none&amp;quot; || style.borderStyle !== &amp;quot;none&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // TODO optimize; can we skip searching candidates or split it up&lt;br /&gt;
    // into multiple frames to avoid blocking the page for some time?&lt;br /&gt;
    const candidates = Array.prototype.filter.call(&lt;br /&gt;
        document.querySelectorAll(&amp;quot;#mw-content-text *&amp;quot;),&lt;br /&gt;
        isCandidate&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    let autoContrastReport = [];&lt;br /&gt;
&lt;br /&gt;
    function autoContrastReportElement(el) {&lt;br /&gt;
        // find top level element within mw-content-text&lt;br /&gt;
        let top = el;&lt;br /&gt;
        while (&lt;br /&gt;
            top.parentElement &amp;amp;&amp;amp;&lt;br /&gt;
            !top.parentElement.classList.contains(&amp;quot;mw-parser-output&amp;quot;)&lt;br /&gt;
        ) {&lt;br /&gt;
            top = top.parentElement;&lt;br /&gt;
        }&lt;br /&gt;
        let nextHeadingLevel = 6;&lt;br /&gt;
        const foundHeadings = Array(nextHeadingLevel + 1);&lt;br /&gt;
        let sectionId, sectionNumber;&lt;br /&gt;
        if (top) {&lt;br /&gt;
            // find elements&lt;br /&gt;
            let previous = top;&lt;br /&gt;
            const recordHeading = function (headingElement, headingLevel) {&lt;br /&gt;
                foundHeadings[headingLevel] = headingElement.textContent;&lt;br /&gt;
                if (sectionId == null) sectionId = headingElement.id;&lt;br /&gt;
                if (sectionNumber == null) {&lt;br /&gt;
                    const editLink =&lt;br /&gt;
                        headingElement.parentElement.querySelector(&lt;br /&gt;
                            &amp;quot;.mw-editsection a&amp;quot;&lt;br /&gt;
                        );&lt;br /&gt;
                    const editUrl = !editLink ? null : new URL(editLink.href);&lt;br /&gt;
                    if (&lt;br /&gt;
                        editUrl &amp;amp;&amp;amp;&lt;br /&gt;
                        editUrl.searchParams &amp;amp;&amp;amp;&lt;br /&gt;
                        editUrl.searchParams.has(&amp;quot;section&amp;quot;)&lt;br /&gt;
                    )&lt;br /&gt;
                        sectionNumber = Number(&lt;br /&gt;
                            editUrl.searchParams.get(&amp;quot;section&amp;quot;)&lt;br /&gt;
                        );&lt;br /&gt;
                }&lt;br /&gt;
            };&lt;br /&gt;
&lt;br /&gt;
            while ((previous = previous.previousElementSibling)) {&lt;br /&gt;
                if (previous.classList.contains(&amp;quot;mw-heading&amp;quot;)) {&lt;br /&gt;
                    // get heading level&lt;br /&gt;
                    const headingElement =&lt;br /&gt;
                        previous.querySelector(&amp;quot;h1,h2,h3,h4,h5,h6&amp;quot;);&lt;br /&gt;
                    if (headingElement) {&lt;br /&gt;
                        const headingLevel = Number(&lt;br /&gt;
                            headingElement.tagName.replace(/h/i, &amp;quot;&amp;quot;)&lt;br /&gt;
                        );&lt;br /&gt;
                        if (headingLevel &amp;amp;&amp;amp; headingLevel &amp;lt;= nextHeadingLevel) {&lt;br /&gt;
                            recordHeading(headingElement, headingLevel);&lt;br /&gt;
                            nextHeadingLevel = headingLevel - 1;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        autoContrastReport.push({&lt;br /&gt;
            headings: foundHeadings&lt;br /&gt;
                .filter(function (v) {&lt;br /&gt;
                    return v != null;&lt;br /&gt;
                })&lt;br /&gt;
                .join(&amp;quot; &amp;gt; &amp;quot;),&lt;br /&gt;
            sectionId: sectionId,&lt;br /&gt;
            sectionNumber: sectionNumber,&lt;br /&gt;
            topClasses: top ? top.className : undefined,&lt;br /&gt;
            classes: el.className,&lt;br /&gt;
            element: el,&lt;br /&gt;
        });&lt;br /&gt;
        el.classList.add(&amp;quot;wikt-auto-contrast-fixed&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function autoContrastFixDone() {&lt;br /&gt;
        /**&lt;br /&gt;
         * Executed when the auto-contrast fix has been applied&lt;br /&gt;
         * to all elements.&lt;br /&gt;
         */&lt;br /&gt;
        if (autoContrastReport.length &amp;gt; 0) {&lt;br /&gt;
            console.log(&lt;br /&gt;
                `Applied auto-contrast fix to ${autoContrastReport.length} elements`&lt;br /&gt;
            );&lt;br /&gt;
            console.log(&amp;quot;Full auto-contrast report&amp;quot;, autoContrastReport);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function doAutoContrastFixOne(el) {&lt;br /&gt;
        /**&lt;br /&gt;
         * Applies the auto-contrast fix to the specified element.&lt;br /&gt;
         */&lt;br /&gt;
        // exemptions&lt;br /&gt;
        if (el.closest(&amp;quot;.wikt-auto-contrast-exempt&amp;quot;)) return;&lt;br /&gt;
        if (el.closest(&amp;quot;.mw-no-invert&amp;quot;)) return;&lt;br /&gt;
&lt;br /&gt;
        const computedStyle = view.getComputedStyle(el);&lt;br /&gt;
&lt;br /&gt;
        // get background and foreground colors.&lt;br /&gt;
        const cssBackgroundColor = computedStyle.backgroundColor;&lt;br /&gt;
        const parsedBackgroundColor = parseCssColor(cssBackgroundColor);&lt;br /&gt;
        if (parsedBackgroundColor == null) return;&lt;br /&gt;
&lt;br /&gt;
        const cssForegroundColor = computedStyle.color;&lt;br /&gt;
        const parsedForegroundColor = parseCssColor(cssForegroundColor);&lt;br /&gt;
        if (parsedForegroundColor == null) return;&lt;br /&gt;
&lt;br /&gt;
        let fixedElement = false;&lt;br /&gt;
&lt;br /&gt;
        const hasDarkTextColor =&lt;br /&gt;
            colorLuminance(parsedForegroundColor) &amp;lt; PARAMETER_MINIMUM_FG_LUMA;&lt;br /&gt;
&lt;br /&gt;
        // check that:&lt;br /&gt;
        // * the background color has enough alpha&lt;br /&gt;
        // * the text color is actually light (or overridden)&lt;br /&gt;
        // * the contrast is bad enough to consider fixing (or dark text color and overridden)&lt;br /&gt;
        // * this isn&amp;#039;t something like an image tag&lt;br /&gt;
        if (&lt;br /&gt;
            parsedBackgroundColor[3] &amp;gt;= PARAMETER_MINIMUM_BG_ALPHA &amp;amp;&amp;amp;&lt;br /&gt;
            (darkTextColorOverride || !hasDarkTextColor) &amp;amp;&amp;amp;&lt;br /&gt;
            (colorContrast(parsedForegroundColor, parsedBackgroundColor) &amp;lt;=&lt;br /&gt;
                PARAMETER_MAXIMUM_CONTRAST_TO_FIX ||&lt;br /&gt;
                (hasDarkTextColor &amp;amp;&amp;amp; darkTextColorOverride)) &amp;amp;&amp;amp;&lt;br /&gt;
            el.tagName !== &amp;quot;IMG&amp;quot;&lt;br /&gt;
        ) {&lt;br /&gt;
            if (&lt;br /&gt;
                colorLuminance(parsedForegroundColor) &amp;lt;&lt;br /&gt;
                PARAMETER_MINIMUM_FG_LUMA&lt;br /&gt;
            ) {&lt;br /&gt;
                // generate a new foreground color and apply it&lt;br /&gt;
                const newForegroundColor = lightenForegroundColor(&lt;br /&gt;
                    parsedForegroundColor&lt;br /&gt;
                );&lt;br /&gt;
                if (&lt;br /&gt;
                    colorLuminance(newForegroundColor) &amp;gt;&lt;br /&gt;
                    colorLuminance(parsedForegroundColor)&lt;br /&gt;
                ) {&lt;br /&gt;
                    fixedElement = true;&lt;br /&gt;
                    el.style.color = makeCssColor(newForegroundColor);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // generate a background color and apply it, but only if it is darker.&lt;br /&gt;
            const newBackgroundColor = darkenBackgroundColor(&lt;br /&gt;
                parsedBackgroundColor&lt;br /&gt;
            );&lt;br /&gt;
            if (&lt;br /&gt;
                colorLuminance(newBackgroundColor) &amp;lt;&lt;br /&gt;
                colorLuminance(parsedBackgroundColor)&lt;br /&gt;
            ) {&lt;br /&gt;
                fixedElement = true;&lt;br /&gt;
                el.style.backgroundColor = makeCssColor(newBackgroundColor);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (computedStyle.borderStyle !== &amp;quot;none&amp;quot; &amp;amp;&amp;amp; PARAMETER_ADJUST_BORDER_COLORS) {&lt;br /&gt;
            // odds are the border color needs the same treatment.&lt;br /&gt;
            const cssBorderColor = view.getComputedStyle(el).borderColor;&lt;br /&gt;
            const parsedBorderColor = parseCssColor(cssBorderColor);&lt;br /&gt;
&lt;br /&gt;
            if (&lt;br /&gt;
                parsedBorderColor != null &amp;amp;&amp;amp;&lt;br /&gt;
                colorLuminance(parsedBorderColor) &amp;gt;=&lt;br /&gt;
                    PARAMETER_MINIMUM_BORDER_LUMA&lt;br /&gt;
            ) {&lt;br /&gt;
                // generate a border color and apply it, but only if it is darker.&lt;br /&gt;
                const newBorderColor = darkenBorderColor(parsedBorderColor);&lt;br /&gt;
                if (&lt;br /&gt;
                    colorLuminance(newBorderColor) &amp;lt;&lt;br /&gt;
                    colorLuminance(parsedBorderColor)&lt;br /&gt;
                ) {&lt;br /&gt;
                    fixedElement = true;&lt;br /&gt;
                    el.style.borderColor = makeCssColor(newBorderColor);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (fixedElement) {&lt;br /&gt;
            // report that we found an element to fix.&lt;br /&gt;
            autoContrastReportElement(el);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function doAutoContrastFixBatch(base) {&lt;br /&gt;
        /**&lt;br /&gt;
         * Applies the auto-contrast fix to the next batch&lt;br /&gt;
         * of elements.&lt;br /&gt;
         */&lt;br /&gt;
        for (let offset = 0; offset &amp;lt; ELEMENTS_PER_FRAME; ++offset) {&lt;br /&gt;
            const index = base + offset;&lt;br /&gt;
            if (index &amp;gt;= candidates.length) {&lt;br /&gt;
                autoContrastFixDone();&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
            doAutoContrastFixOne(candidates[index]);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        window.requestAnimationFrame(function () {&lt;br /&gt;
            doAutoContrastFixBatch(base + ELEMENTS_PER_FRAME);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    doAutoContrastFixBatch(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$(document).ready(function () {&lt;br /&gt;
    doAutoContrastFix();&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
// &amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>wikt&gt;Surjection</name></author>
	</entry>
</feed>