您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
AtCoder Problems 上に表示される問題にユーザが独自のマーカー(解説ACなど)を付けられるようにします
- // ==UserScript==
- // @name AtCoder Problems Marker
- // @namespace iilj
- // @version 2020.6.26.1
- // @description AtCoder Problems 上に表示される問題にユーザが独自のマーカー(解説ACなど)を付けられるようにします
- // @author iilj
- // @supportURL https://github.com/iilj/AtCoderProblemsExt/issues
- // @match https://kenkoooo.com/atcoder/*
- // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.contextMenu.min.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.ui.position.js
- // @resource css_contextmenu https://cdnjs.cloudflare.com/ajax/libs/jquery-contextmenu/2.7.1/jquery.contextMenu.min.css
- // @grant GM_addStyle
- // @grant GM_getResourceText
- // ==/UserScript==
- /* globals $ */
- (function () {
- 'use strict';
- GM_addStyle(GM_getResourceText('css_contextmenu'));
- GM_addStyle(`
- td.table-problem.apm-ac-after-reading-answer {
- background-color: #ffff88 !important;
- }
- `);
- /**
- * key for localStorage to save ac-after-reading-answer state
- * @type {string}
- */
- const localStorageKey = "apm-hashset";
- /**
- * json string loaded from localStorage, or to save on localStorage
- * @type {string}
- */
- let json;
- /**
- * hashset to store whether the problem is marked as ac-after-reading-answer or not
- * @type {{[key:string]: number}}
- */
- let hash;
- /**
- * user id of AtCoder
- * @type {string}
- */
- let userId;
- /**
- * load ac-after-reading-answer state from localStorage
- */
- const loadHash = () => {
- json = localStorage.getItem(localStorageKey);
- hash = json ? JSON.parse(json) : {};
- }
- /**
- * save ac-after-reading-answer state to localStorage
- */
- const saveHash = () => {
- json = JSON.stringify(hash);
- localStorage.setItem(localStorageKey, json);
- }
- /**
- * url of problem -> key of hash
- *
- * @param {string} href
- * @returns {string | null} key of hash if valid url supplied, otherwise null
- */
- const href2key = (href) => {
- let result
- if (result = href.match(/^https?:\/\/atcoder\.jp\/contests\/([^\/]+)\/tasks\/([^\/]+)$/)) {
- return `${userId}/${result[1]}/${result[2]}`;
- }
- return null;
- };
- $.contextMenu({
- selector: '.table-problem',
- callback: (key, options) => {
- switch (key) {
- case "ac_after_reading_answer": {
- const td = options.$trigger;
- const href = td.find('a[href]').attr('href');
- const key = href2key(href);
- if (key in hash) {
- delete hash[key];
- } else {
- hash[key] = 0;
- }
- td.toggleClass('apm-ac-after-reading-answer');
- saveHash();
- break;
- }
- case "about":
- alert("解説ACした問題を Table ページ上でマークできます.マークした結果はブラウザに保存されます.");
- }
- },
- items: {
- "ac_after_reading_answer": { name: "解説AC On/Off" },
- "sep1": "---------",
- "about": { name: "About" }
- }
- });
- /**
- * Table 表示ページで "Show Accepted" の変更検知に利用する MutationObserver
- *
- * @type {MutationObserver}
- */
- let tableObserver;
- /**
- * Table 表示ページで表のセルの色を塗り分ける.
- *
- * @date 2020-01-27
- * @param {string} userId
- */
- const processTable = () => {
- const tableChanged = () => {
- if (tableObserver) {
- tableObserver.disconnect();
- }
- document.querySelectorAll('.table-problem').forEach(td => {
- const lnk = td.querySelector('a[href]');
- if (!lnk) {
- return;
- }
- const key = href2key(lnk.href);
- if (!key) {
- return;
- }
- if (key in hash) {
- td.classList.add('apm-ac-after-reading-answer');
- }
- });
- if (tableObserver) {
- document.querySelectorAll('.react-bs-container-body').forEach(div => {
- tableObserver.observe(div, { childList: true, subtree: true });
- });
- }
- };
- tableObserver = new MutationObserver(mutations => tableChanged());
- tableChanged();
- document.querySelectorAll('.react-bs-container-body').forEach(div => {
- tableObserver.observe(div, { childList: true, subtree: true });
- });
- }
- /**
- * ページ URL が変化した際のルートイベントハンドラ.
- *
- * @date 2020-01-27
- */
- const hrefChanged = () => {
- if (tableObserver) {
- tableObserver.disconnect();
- }
- /** @type {RegExpMatchArray} */
- let result;
- if (result = location.href.match(/^https?:\/\/kenkoooo\.com\/atcoder\/#\/table\/([^/?#]+)/)) {
- userId = result[1];
- processTable();
- }
- };
- // main
- loadHash();
- let href = location.href;
- const observer = new MutationObserver(mutations => {
- if (href === location.href) {
- return;
- }
- // href changed
- href = location.href;
- hrefChanged();
- });
- observer.observe(document, { childList: true, subtree: true });
- hrefChanged();
- })();