User:Isaac (WMF)/clickstream viz.js

// Avoid triggering: https://en.wikipedia.org/wiki/Special:WantedTemplates // Code largely borrowed from: https://en.wikipedia.org/wiki/User:TayIorRobinson/wikigender.js

/** * @typedef ClickstreamLink * @typedef ClickstreamData */ //

(async => {	// https://wikinav.wmcloud.org/api/v1/en/Chocolate/destinations/latest?limit=10   const CLICKSTREAM_API = "https://wikinav.wmcloud.org/api/v1/";    async function fetchClickstreamLangs {        let response = await fetch("https://wikinav.wmcloud.org/api/v1/latest/meta");        let data = await response.json;        return data.languages;    }

/**    * Requests the clickstream data for the current page. * @param {String} lang Wikipedia language to query. * @returns {Promise} The latest clickstream data for the page. */   async function fetchData(lang) { let title = mw.config.get("wgPageName"); let response = await fetch(CLICKSTREAM_API + lang + "/" + title + "/destinations/latest?limit=1000"); let data = await response.json; return data; }   async function fetchRedirects { let redirects = {}; let linksToRedirects = document.querySelectorAll("a.mw-redirect"); if (linksToRedirects.length > 0) { let joinedTitles = ""; let titlesInQuery = new Set; for (let i = 0; i < linksToRedirects.length; i++) { if (titlesInQuery.has(linksToRedirects[i].title)) continue; titlesInQuery.add(linksToRedirects[i].title); joinedTitles += linksToRedirects[i].title + "|"; // Process first 50 (single API call max) if (titlesInQuery.size == 50) break; }			// remove final | character joinedTitles = joinedTitles.slice(0, -1); let lang = mw.config.get("wgPageContentLanguage"); let response = await fetch("https://" + lang + ".wikipedia.org/w/api.php?action=query&format=json&formatversion=2&redirects&titles=" + joinedTitles); let data = await response.json; for (let rd of data.query.redirects) { redirects[rd.from.toLowerCase.replace(/_/g, " ")] = rd.to.toLowerCase.replace(/_/g, " "); }   	}        return redirects; }   /**     * Requests the clickstream data for the current page. * @param {Number} num_views Number of clicks to that link. * @param {Number} max_views Maximum number of clicks to any link. * @returns {String} Alpha value as string based on proportion of pageviews via link. [0.2 - 1]    */    function propPageviewsToAlpha(num_views, max_views) { if (num_views == 0) { return '0'; } else { let alpha = 0.1 + (0.9 * num_views / max_views); return alpha.toFixed(2); }   }

/**    * Applies colour to links on the page * @param {ClickstreamLink[]} links The links to colour. */   function applyLinkColours(links, redirects) { let pageLinks = document.querySelector("#bodyContent").querySelectorAll("a[href^=\"/wiki/\"]"); let max_views = 0; // 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, " ")); // map redirects to canonical title (used by clickstream) if (linkText in redirects) { linkText = redirects[linkText]; }           // Build array of all instances of a link appearing on page if (!(linkText in linksOnPage)) { linksOnPage[linkText] = []; }           linksOnPage[linkText].push(link); }       for (let link of links) { max_views = Math.max(max_views, link.views); }       // Colour the links. for (let link of links) { let linkOnPage = linksOnPage[link.title.toLowerCase.replace(/_/g, " ")]; if (!linkOnPage) continue; let alpha = propPageviewsToAlpha(link.views, max_views); for (let l of linkOnPage) { l.style.backgroundColor = "rgba(216,127,165," + alpha + ")"; }       }    }

// article but not Main Page (links change too frequently to be relevant to data) if (mw.config.get('wgNamespaceNumber') == 0 && mw.config.get('wgWikibaseItemId') != 'Q5296') { let lang = mw.config.get("wgDBname").replace("wiki",""); let csLangs = await fetchClickstreamLangs; if (csLangs.includes(lang)) { let mwIndicator = document.createElement("div"); mwIndicator.className = "mw-indicator"; mwIndicator.id = "mw-indicator-clickstream"; 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(lang); let redirects = await fetchRedirects; applyLinkColours(data.results, redirects); };		   link.title = "Visualize the clicks to the links on the page"; mwIndicator.appendChild(link); let img = document.createElement("img"); img.alt = "Data analytics icon"; img.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/86/Cs%2B.svg/41px-Cs%2B.svg.png"; img.decoding = "async"; img.height = 20; img.width = 20; link.appendChild(img); }   } });