您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
AtCoder の順位表に最多提出言語を追加します.uesugi6111 さん作のスクリプトが元ネタです.
当前为
- // ==UserScript==
- // @name atcoder-standings-lang
- // @namespace iilj
- // @version 2020.11.10.0
- // @description AtCoder の順位表に最多提出言語を追加します.uesugi6111 さん作のスクリプトが元ネタです.
- // @author iilj
- // @supportURL https://github.com/iilj/atcoder-standings-lang/issues
- // @match https://atcoder.jp/contests/*/standings*
- // ==/UserScript==
- /* globals $ */
- /**
- * ユーザID/言語ごとの提出数
- * @typedef {Object} UserLangEntry
- * @property {string} user_id ユーザ ID
- * @property {string} language 言語名
- * @property {number} count 提出数
- */
- (() => {
- 'use strict';
- /** @type {Map<string, UserLangEntry[]>} */
- const userLangEntryMap = new Map();
- const fetchLangJson = async () => {
- console.log('@kenkooooさんありがとう');
- // 2e5 個くらい要素があるのでキャッシュする
- const res = await fetch("https://kenkoooo.com/atcoder/resources/lang.json", { cache: 'force-cache' });
- /** @type {UserLangEntry[]} */
- const userLangEntries = await res.json();
- // prepare map
- userLangEntries.forEach(userLangEntry => {
- if (userLangEntryMap.has(userLangEntry.user_id)) {
- userLangEntryMap.get(userLangEntry.user_id).push(userLangEntry);
- } else {
- userLangEntryMap.set(userLangEntry.user_id, [userLangEntry]);
- }
- });
- // sort arrays
- userLangEntryMap.forEach(userLangArray => {
- userLangArray.sort((a, b) => b.count - a.count); // in place
- });
- };
- /** @type {(anchor: HTMLAnchorElement) => void} */
- const updateAnchor = (anchor) => {
- if (!anchor.href.includes('/users/')) return;
- const user_id = anchor.text.trim();
- if (!userLangEntryMap.has(user_id)) return;
- const userLangArray = userLangEntryMap.get(user_id);
- const tooltipHtml = userLangArray.map((userLangEntry) =>
- `${userLangEntry.language} : ${userLangEntry.count}`
- ).join('<br>');
- let langHtml = userLangArray[0].language;
- if (userLangArray.length >= 2) {
- langHtml += '<div style="font-size:10px;display:inline;">'
- + `/${userLangArray[1].language}`
- + (userLangArray.length >= 3 ? `/${userLangArray[2].language}` : '')
- + ' </div>';
- }
- anchor.insertAdjacentHTML('beforeend',
- '/'
- + '<div data-toggle="tooltip" data-html="true" data-placement="right" style="font-size:12px;display:inline;" title="' + tooltipHtml + '">'
- + langHtml
- + '</div>');
- $('[data-toggle="tooltip"]').tooltip();
- };
- /** @type {(tbody: HTMLTableSectionElement) => void} */
- const updateTable = (tbody) => {
- tbody.querySelectorAll('.username').forEach(anchor => {
- updateAnchor(anchor);
- });
- };
- /** @type {HTMLTableSectionElement} */
- let tbody = null;
- const tableObserver = new MutationObserver(() => {
- updateTable(tbody);
- });
- const parentObserver = new MutationObserver(async () => {
- tbody = document.getElementById('standings-tbody');
- if (tbody) {
- parentObserver.disconnect();
- await fetchLangJson();
- updateTable(tbody);
- tableObserver.observe(
- tbody,
- { childList: true }
- )
- }
- });
- parentObserver.observe(
- document.getElementById('vue-standings'),
- { childList: true, subtree: true }
- );
- })();