您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
AtCoderの提出結果一覧画面に自動検索機能などを追加します。
// ==UserScript== // @name AtCoder ResultsPage Tweaks // @namespace https://github.com/yukuse // @version 1.0.4 // @description AtCoderの提出結果一覧画面に自動検索機能などを追加します。 // @author yukuse // @include https://atcoder.jp/contests/*/submissions // @include https://atcoder.jp/contests/*/submissions?* // @include https://atcoder.jp/contests/*/submissions/me // @include https://atcoder.jp/contests/*/submissions/me?* // @grant window.jQuery // @grant window.fixTime // @license MIT // ==/UserScript== // jQueryカスタムイベントを監視/発火するためwindowのjQueryを使用しています。 jQuery(($) => { const options = { // 検索条件変更時に自動検索 on/off autoSearchOnChange: true, // 検索結果の動的読み込み(少し検索結果表示が早くなる) on/off // NOTE: trueのとき、他のスクリプトによる表示変更(AtCoder Difficulty Displayなど)が反映されないため注意! dynamicResult: false, // 検索条件変更時のフォーカス維持 on/off keepSelectFocus: true, // 画面表示時のフォーカス対象 '#select-task'|'#select-language'|'#select-status'|'#input-user'|null focusOnVisit: '#select-task', // 言語固定中の検索時に言語がリセットされないようにする keepLanguage: true, }; const $container = $('#main-container'); const $panelSubmission = $container.find('.panel-submission'); const baseParams = { 'f.Task': '', 'f.Language': '', 'f.LanguageName': '', 'f.Status': '', 'f.User': '', page: 1, orderBy: '', desc: '', }; function parseSubmissionsUrl(url) { const params = { ...baseParams }; if (url) { Object.keys(params).forEach((key) => { const regexp = new RegExp(`${key}=([^&]+)`); const result = url.match(regexp); if (result) { [, params[key]] = result; } }); } return params; } /** * 現在のURLに応じて検索結果表示を更新 * TODO: ジャッジ中表示対応 */ function updateSearchResult() { const { href } = location; const params = parseSubmissionsUrl(href); // 検索条件を遷移先の状態にする Object.keys(params).forEach((key) => { $panelSubmission.find(`[name="${key}"]`).val(params[key]).trigger('change'); }); const $tmp = $('<div>'); $tmp.load(`${href} #main-container`, '', () => { const $newTable = $tmp.find('.table-responsive, .panel-body'); // テーブル置換 $panelSubmission.find('.table-responsive, .panel-body').replaceWith($newTable); // ページネーション置換 if ($newTable.hasClass('table-responsive')) { $container.find('.pagination').replaceWith($tmp.find('.pagination:first')); } else { $container.find('.pagination').empty(); } // 日付を表示 fixTime(); }); } /** * 検索条件を元にURLを更新し、結果を表示する */ function showSearchResult(params) { const paramsStr = Object.keys(params).map((key) => `${key}=${params[key]}`).join('&'); const url = `${location.pathname}?${paramsStr}`; if (options.dynamicResult) { history.pushState({}, '', url); updateSearchResult(); } else { location.href = url; } } /** * フォームに設定されたパラメータを取得 */ function getFormParams() { const params = { ...baseParams }; Object.keys(params).forEach((key) => { params[key] = $panelSubmission.find(`[name="${key}"]`).val(); // 空のキーは外す if (!params[key]) { delete params[key]; } }); params.page = 1; return params; } /** * フォームの検索条件で検索 */ function search() { showSearchResult(getFormParams()); } /** * 選択欄の調整 * - 選択時に自動検索 * - 画面表示時に選択欄自動フォーカス * - 選択時にフォーカスが飛ばないようにする */ function initSelectTweaks() { // 選択欄自動フォーカス if (options.focusOnVisit) { $panelSubmission.find(options.focusOnVisit).focus(); } $panelSubmission.find('#select-task, #select-language, #select-status').on('select2:select select2:unselect', (event) => { // unselectの場合は選択状態が遅れて反映されるため、実行を遅らせる setTimeout(() => { // 選択時に自動検索 if (options.autoSearchOnChange) { search(); } // 選択時にフォーカスが飛ばないようにする if (options.keepSelectFocus) { event.target.focus(); } }, 0); }); } const urlRegExp = new RegExp(`${location.pathname}[^/]*$`); /** * 検索結果のリンククリック時のページ遷移をなくし、表示を動的に更新する処理に置き換え */ function initLinks() { $container.on('click', '.pagination a, .panel-submission a', (event) => { const { href } = event.target; if (!urlRegExp.test(href)) { return; } // 言語リンクは除外 if (/f.Language=([^&]+)/.test(href)) { return; } event.preventDefault(); showSearchResult(parseSubmissionsUrl(href)); }); } function init() { initSelectTweaks(); if (options.dynamicResult) { window.addEventListener('popstate', updateSearchResult); initLinks(); // フォームの検索押下時に検索結果を動的読み込み $panelSubmission.find('form').on('submit', (event) => { event.preventDefault(); search(); }); } // 言語固定中の検索時に言語がリセットされないようにする if (options.keepLanguage) { const result = location.href.match(/f.Language=(\d+)/); if (result && result[1]) { const inputHidden = $(`<input type="hidden" name="f.Language" value="${result[1]}">`); $panelSubmission.find('form').append(inputHidden); } } } init(); });