User:Isaac (WMF)/link gender.js

// Avoid triggering: https://en.wikipedia.org/wiki/Special:WantedTemplates // Slightly tweaked version of original script: https://en.wikipedia.org/wiki/User:TayIorRobinson/wikigender.js // Developed as part of Wikimania 2021 Hackathon: https://phabricator.wikimedia.org/T288666

/** * @typedef {HTMLDivElement} GenderBar * @typedef {'male' | 'female' | 'nb' | 'none'} Gender * @typedef GenderedLink * @typedef GenderSummary * @typedef GenderData */ //

(async => {   const GENDERDATA_OUTLINKS_API = "https://article-link-gender-data.toolforge.org/api/v1/outlinks-details";    const GENDERDATA_INLINKS_API = "https://article-link-gender-data.toolforge.org/api/v1/inlinks-details";

/**    * Requests the gender data for the current page. * @returns {Promise} The analysed gender data for the page. */   async function fetchData { let lang = location.host.split(".")[0]; let article; let response; if (mw.config.get('wgCanonicalSpecialPageName') == 'Whatlinkshere') { article = location.pathname.split("/")[3]; response = await fetch(GENDERDATA_INLINKS_API + "?lang=" + lang + "&title=" + article); } else { article = location.pathname.split("/")[2]; response = await fetch(GENDERDATA_OUTLINKS_API + "?lang=" + lang + "&title=" + article); }       let data = await response.json;

return data; }

/**    * Creates an instance of a gender bar on the current page. * @returns {GenderBar} */   function createGenderBar { let element = document.createElement("div"); element.id = "genderbar"; element.style.maxHeight = "5px"; element.style.width = "70%"; element.style.marginTop = "-0.5em"; element.style.overflow = "hidden"; element.onmouseenter = => element.style.maxHeight = "1000px"; element.onmouseleave = => element.style.maxHeight = "5px";

document.querySelector("#mw-content-text").insertAdjacentElement("afterbegin", element); return element; }

/**    * Takes a gender and gives back a hex colour code * @param {string} name * @returns {string} */   function getGenderColour(name) { switch (name) { case "N/A": return "#EEEEEE"; case "male": return "#517FC1"; case "transgender male": case "transmasculine": return "#3d5f8f"; case "female": return "#F19359"; case "transgender female": case "transfeminine": return "#cf7d4a"; default: return "#FAD965"; }   }    /* Order the genders so it looks nicer on the graph */ const ORDER = [ "female", "transgender female", "transfeminine", "male", "transgender male", "transmasculine", "N/A" ];

/**    * Make rows for each gender category for the table * @param {GenderSummary[]} literals * @returns {string} */   function makeRows(list) { return ` ${list.map((o) => ` ${o.gender} `).join("")} ${list.map((o) => o.num_links.toLocaleString + " link" + (o.num_links != 1 ? "s" : "")).join(" ")} ${list.map((o) => Math.round(o.pct_links * 100) + "%").join(" ")} `; }

/**    * Fills a genderbar with a gender summary. * @param {GenderBar} genderbar The gender bar to fill. * @param {GenderSummary[]} summaries The summary of the genders. */   function fillGenderBar(genderbar, summaries) { genderbar.innerHTML = ""; // Clear the gender bar of any previous elements.

let x = 0; let gradientParts = []; for (let summary of summaries.sort((a, b) => ORDER.indexOf(a.gender) - ORDER.indexOf(b.gender))) { gradientParts.push(getGenderColour(summary.gender) + " " + x + "%"); x += summary.pct_links * 100; gradientParts.push(getGenderColour(summary.gender) + " " + x + "%"); }       genderbar.style.backgroundImage = "linear-gradient(to right, " + gradientParts.join(", ") + ")";

// Fill table let maleList = []; let femaleList = []; let nbList = []; for (let summary of summaries) { switch (summary.gender) { case "N/A": break; case "male": case "transgender male": case "transmasculine": maleList.push(summary); break; case "female": case "transgender female": case "transfeminine": femaleList.push(summary); break; default: nbList.push(summary); break; }       }        let table = document.createElement("table"); table.style.width = "100%"; table.style.background = "#eee"; table.style.paddingTop = "5px"; table.style.backgroundClip = "content-box"; table.style.borderSpacing = "0.5em"; // I will admit the following code is **not** pretty. // I am less than proud. table.innerHTML = ` ${true ? ` Non-Binary Genders ${nbList.reduce((previous, value) => previous + value.num_links, 0).toLocaleString} links ${Math.round(nbList.reduce((previous, value) => previous + value.pct_links, 0) * 100)}% ` : ""} ${true ? ` Female ${femaleList.reduce((previous, value) => previous + value.num_links, 0).toLocaleString} links ${Math.round(femaleList.reduce((previous, value) => previous + value.pct_links, 0) * 100)}% ` : ""} ${true ? ` Male ${maleList.reduce((previous, value) => previous + value.num_links, 0).toLocaleString} links ${Math.round(maleList.reduce((previous, value) => previous + value.pct_links, 0) * 100)}% ` : ""}  ${true ? makeRows(nbList) : ""} ${true ? makeRows(femaleList) : ""} ${true ? makeRows(maleList) : ""} `;       genderbar.appendChild(table); }

/**    * Applies colour to links on the page * @param {GenderedLink[]} links The links to colour. */   function applyLinkColours(links) { let pageLinks; if (mw.config.get('wgCanonicalSpecialPageName') == 'Whatlinkshere') { pageLinks = document.querySelector("#mw-whatlinkshere-list").querySelectorAll("a[href^=\"/wiki/\"]"); } else { pageLinks = document.querySelector("#bodyContent").querySelectorAll("a[href^=\"/wiki/\"]"); }       // Create an object of all the links on the page. let linksOnPage = {}; for (let link of pageLinks) { let linkText = decodeURIComponent(link.href.split("/")[4].toLowerCase.replace(/_/g, " ")); // Build array of all instances of a link appearing on page if (!(linkText in linksOnPage)) { linksOnPage[linkText] = []; }           linksOnPage[linkText].push(link); }       // Colour the links. for (let link of links) { let linkOnPage = linksOnPage[link.title]; if (!linkOnPage) continue; for (let l of linkOnPage) { l.style.backgroundColor = getGenderColour(link.gender) + "88"; }       }    }

if (location.host.split(".")[1] == "wikipedia") { let mwIndicator = document.createElement("div"); mwIndicator.className = "mw-indicator"; mwIndicator.id = "mw-indicator-gender"; document.querySelector(".mw-indicators").appendChild(mwIndicator); let link = document.createElement("a"); link.href = "javascript:void(0)"; link.onclick = async => { link.remove; let data = await fetchData; fillGenderBar(createGenderBar, data.summary); applyLinkColours(data.details); };	   link.title = "Analyse the gender of the links on the page"; mwIndicator.appendChild(link); let img = document.createElement("img"); img.alt = "Gender equality icon"; img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Gender_equality.png/20px-Gender_equality.png"; img.srcset = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Gender_equality.png/30px-Gender_equality.png 1.5x, https://upload.wikimedia.org/wikipedia/commons/thumb/d/d7/Gender_equality.png/40px-Gender_equality.png 2x"; img.decoding = "async"; img.height = 20; img.width = 20; link.appendChild(img); } });