OMC Standings Search

OMCでユーザー名を検索できます

// ==UserScript==
// @name         OMC Standings Search
// @namespace    https://onlinemathcontest.com/
// @version      1.0
// @description  OMCでユーザー名を検索できます
// @match        https://onlinemathcontest.com/contests/*/standings*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  if (!/\/contests\/[^/]+\/standings/.test(location.href)) return;

  const contestMatch = location.href.match(/contests\/([^/]+)\/standings/);
  if (!contestMatch) return;

  const contestId = contestMatch[1];
  const standingsApi = `/api/contests/${contestId}/standings`;

  fetch(`${standingsApi}?rated=0`)
    .then(res => res.json())
    .then(data => {
      const standingsEl = document.getElementById('standings');
      if (!standingsEl) return;

      const { isPast, is_point_visible } = data;
      if (!(isPast && is_point_visible)) return;

      const container = document.querySelector('div.container');
      const form = container?.querySelector('form');

      if (form) {
        form.insertAdjacentHTML('afterend', `
          <div class="form-inline my-2" style="margin-bottom:1em;">
            <input type="text" id="userSearchInput" placeholder="ユーザー名を入力" class="form-control mr-2">
            <button id="userSearchBtn" class="btn btn-primary">Search</button>
            <span id="userSearchStatus" style="margin-left:1em;color:#666;"></span>
          </div>
        `);
      }

      const inputEl = document.getElementById('userSearchInput');
      const searchBtn = document.getElementById('userSearchBtn');
      const statusEl = document.getElementById('userSearchStatus');

      const isRatedChecked = () => document.getElementById('ratedCheckbox')?.checked ? 1 : 0;

      async function searchUser(giveup) {
        const targetName = inputEl.value.trim().toLowerCase();
        if (!targetName) return;

        searchBtn.disabled = true;
        statusEl.textContent = '';

        try {
          const res = await fetch(`${standingsApi}?rated=${isRatedChecked()}`, { credentials: 'same-origin' });
          const { standings } = await res.json();

          const userIndex = standings.findIndex(s => s.user.id.toLowerCase() === targetName);
          if (userIndex === -1) {
            searchBtn.disabled = false;
            return;
          }

          const targetPage = Math.floor(userIndex / 20) + 1;

          const navigateToPage = () => {
            for (let trying=0; trying<10;trying++){
                const currentPage = parseInt(document.querySelector('.page-item.active .page-link')?.textContent.trim(), 10);

                if (currentPage === targetPage) {
                    setTimeout(() => {
                        const rows = document.querySelectorAll('tbody tr');
                        let found = false;

                        rows.forEach(row => {
                            const link = row.querySelector('a.user-link');
                            const rank = parseInt(row.children[0].textContent, 10);

                            if (link?.textContent.trim().toLowerCase() === targetName && rank === standings[userIndex].rank) {
                                found = true;
                                Array.from(row.children).forEach(cell => {
                                    if (!cell.classList.contains('table-danger')) {
                                        cell.style.backgroundColor = '#f4f6d8';
                                    }
                                });
                                row.scrollIntoView({ behavior: 'smooth', block: 'center' });
                            } else {
                                Array.from(row.children).forEach(cell => {
                                    if (window.getComputedStyle(cell, null).getPropertyValue('background-color')==='rgb(244, 246, 216)') {
                                        cell.style.backgroundColor = '#ffffff';
                                    }
                                });
                            }
                        });
                        searchBtn.disabled = false
                        if (!found && !giveup){
                            retrySearch();
                        }
                    }, 10);
                    return;
                }

                const pageItems = [...document.querySelectorAll('ul.pagination .page-item')];
                const closest = pageItems.reduce((best, item) => {
                    const pageNum = parseInt(item.querySelector('.page-link')?.textContent.trim(), 10);
                    const diff = Math.abs(pageNum - targetPage);
                    return (!isNaN(pageNum) && diff < best.diff) ? { item, diff } : best;
                }, { item: null, diff: Infinity });

                if (closest.item) {
                    closest.item.querySelector('.page-link').click();
                    //setTimeout(navigateToPage, 50);
                } else {
                    searchBtn.disabled = false;
                }
            }
            retrySearch();
          };

          navigateToPage();
        } catch (e) {
          console.error(e);
          searchBtn.disabled = false;
        }
      }

      function retrySearch() {
        const nextBtn = document.querySelector('.btn.btn-outline-secondary');
        if (!nextBtn) return;

        nextBtn.click();

        const waitUntilReady = () => {
          if (nextBtn.disabled) {
            searchUser(true);
          } else {
            setTimeout(waitUntilReady, 50);
          }
        };

        waitUntilReady();
      }

      searchBtn.addEventListener('click', searchUser);
      inputEl.addEventListener('keydown', e => {
        if (e.key === 'Enter') {
          e.preventDefault();
          searchUser(false);
        }
      });
    })
    .catch(err => console.error('Standings fetch error:', err));
})();
长期地址
遇到问题?请前往 GitHub 提 Issues,或加Q群1031348184

赞助商

Fishcpy

广告

Rainyun

注册一下就行

Rainyun

一年攒够 12 元