SYZOJ ranklist 溢出修复
前言
这个系列怎么更新这么多期了?感兴趣的读者可以去阅读前面的文章。
正文
在 SYZOJ,当一场比赛的题目过多时,ranklist 展示界面表格会溢出,且不能滚动看到右侧题目的得分情况,必须手动缩放整个页面,十分麻烦,不友好。


我们可以简单调整以下做到如下效果,打开页面自动缩放:

当然可以点击按钮放大至无缩放查看:

当然如果不缩放不会溢出,那么默认无缩放。
代码
css 没有变更,可以参考上一篇文章。
JS 代码
// ==UserScript==
// @name SYZOJ darker
// @namespace http://tampermonkey.net/
// @version 2025-08-05
// @description use custom dark style for SYZOJ
// @author XuYueming
// @match http://?/*
// @grant unsafeWindow
// ==/UserScript==
(function () {
'use strict';
function copyText(text) {
return new Promise((resolve, reject) => {
if (!text) {
reject('nothing copyied!');
}
const $textarea = $('<textarea>');
$('body').append($textarea);
$textarea.val(text).select();
try {
const successful = document.execCommand('copy');
$textarea.remove();
if (successful) {
resolve();
} else {
reject();
}
} catch (err) {
$textarea.remove();
reject(err);
}
});
}
const createEditorIframe = function (code, lang, $parent, config) {
const $iframe = $('<iframe>')
.attr('class', 'custom-editor')
.appendTo($parent);
const iframe = $iframe[0];
const win = iframe.contentWindow;
const doc = iframe.contentDocument || iframe.contentWindow.document;
doc.open();
doc.write(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
html, body, #container {
margin: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
</head>
<body>
<div id="container"></div>
</body>
</html>
`);
doc.close();
return new Promise((resolve) => {
const tryInit = () => {
if (typeof win.require === 'undefined') {
setTimeout(tryInit, 50); // retry
return;
}
win.require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@0.44.0/min/vs' } });
win.require(['vs/editor/editor.main'], function () {
const editor = win.monaco.editor.create(doc.getElementById('container'), {
value: code,
language: lang,
theme: 'vs-dark',
contextmenu: false,
renderFinalNewline: true,
scrollbar: {
alwaysConsumeMouseWheel: false,
},
...config
});
resolve({
$iframe: $iframe,
editor: editor,
winMonaco: win.monaco
});
});
};
tryInit();
});
};
const problemEditor = async function () {
const code = unsafeWindow.editor.getValue();
const lang = unsafeWindow.editor.getModel().getLanguageIdentifier().language;
const { $iframe, editor, winMonaco } = await createEditorIframe(code, lang, $('#editor'));
unsafeWindow.editor.dispose();
unsafeWindow.editor = editor;
$('#languages-menu .item').click(function () {
winMonaco.editor.setModelLanguage(editor.getModel(), $(this).data('mode'));
});
};
const submissionEditor = async function () {
const script = document.createElement('script');
script.textContent = `
// 尝试暴露页面中的变量
(function() {
function decodeHtmlEntities(str) {
const doc = new DOMParser().parseFromString(str, 'text/html');
return doc.documentElement.textContent;
}
window._exposed = {
formattedCode: formattedCode && decodeHtmlEntities(formattedCode),
unformattedCode: unformattedCode && decodeHtmlEntities(unformattedCode),
language: vueApp.roughData.info['language']
};
})();
`;
document.documentElement.appendChild(script);
script.remove();
await new Promise(resolve => setTimeout(resolve, 50));
const { formattedCode, unformattedCode, language } = unsafeWindow._exposed;
let lang = undefined;
if (language.includes('C++')) {
lang = 'cpp';
} else if (language.includes('Python')) {
lang = 'python';
} else {
console.warn('unknown language', language);
return;
}
const $div = $('<div>');
$($('#submission_content pre:has(>code)')[0])
.replaceWith($div);
const { $iframe, editor } = await createEditorIframe(formattedCode || unformattedCode, lang, $div,
{
minimap: { enabled: false },
scrollBeyondLastLine: false,
readOnly: true,
automaticLayout: false,
scrollbar: {
vertical: 'hidden',
alwaysConsumeMouseWheel: false,
},
}
);
const updateHeight = () => {
const contentHeight = editor.getContentHeight() + 5;
$div.css({ height: `${contentHeight}px` });
editor.layout();
};
updateHeight();
if (formattedCode) {
const $btn = $div.parent().find('>a');
let formatted = true;
$btn.on('click', () => {
formatted = !formatted;
editor.setValue(formatted ? formattedCode : unformattedCode);
updateHeight();
});
}
editor.focus();
};
const copyMarkdown = async function () {
function getHeaderLevel($x) {
const m = $x.prop("tagName").match(/^H([1-6])$/);
return m ? +m[1] : null;
}
function tableToMarkdownWithSpan($table) {
const grid = [];
let maxCols = 0;
$table.find('tr').each(function (rowIndex) {
grid[rowIndex] = grid[rowIndex] || [];
let colIndex = 0;
$(this).children('th, td').each(function () {
// 跳过已占用的位置
while (grid[rowIndex][colIndex]) colIndex++;
const cell = $(this);
const text = getText(cell).trim().replace(/\n/g, '<br>');
const rowspan = parseInt(cell.attr('rowspan') || 1);
const colspan = parseInt(cell.attr('colspan') || 1);
for (let r = 0; r < rowspan; r++) {
for (let c = 0; c < colspan; c++) {
const targetRow = rowIndex + r;
const targetCol = colIndex + c;
grid[targetRow] = grid[targetRow] || [];
grid[targetRow][targetCol] = text; // 使用相同文本
}
}
colIndex += colspan;
maxCols = Math.max(maxCols, colIndex);
});
});
// 构建 Markdown 表格
let markdown = '';
for (let i = 0; i < grid.length; i++) {
const row = grid[i];
const paddedRow = [];
for (let j = 0; j < maxCols; j++) {
paddedRow.push(row[j] !== undefined ? row[j] : '');
}
markdown += '| ' + paddedRow.join(' | ') + ' |\n';
// 添加分隔线
if (i === 0) {
markdown += '| ' + paddedRow.map(() => '---').join(' | ') + ' |\n';
}
}
return markdown;
}
const getText = function ($content) {
if ($content.is('span.mjpage')) { // math formula
const math = $content.find('title').text().replace(/^\n+|\n+$/g, ''); // remove '\n' but not ' ', see this $\ $
return $content.is('.mjpage__block') ? `\n\$\$\n${math}\n\$\$\n` : ` $${math}$ `;
}
if ($content.is('code')) { // code
if ($content.is(':not(pre) > code'))
return '`' + $content.text() + '`';
return '```\n' + $content.text() + '```\n\n';
}
if ($content.is('table')) { // table
return '\n\n' + tableToMarkdownWithSpan($content) + '\n\n';
}
if ($content.is('ul') || $content.is('ol')) { // list
const prefix = $content.is('ul') ? '- ' : '1. ';
return $content.children().map((_, li) => prefix + getText($(li)).trim().replace(/\n/g, '\n ')).get().join('\n') + '\n';
}
if ($content.is('hr')) { // horizon
return '------\n';
}
if ($content.is('br')) { // new line
return '\n';
}
let text = "";
$content.contents().each(function (index, node) {
if (node.nodeType === 3) {
text += node.nodeValue;
} else if (node.nodeType === 1) {
text += getText($(node));
}
});
if ($content.is('strong')) { // strong
text = `**${text}**`;
}
if ($content.is('s')) { // delete
text = `~~${text}~~`;
}
if ($content.is('em')) { // em
text = `_${text}_`;
}
if (getHeaderLevel($content)) { // header
text = `${'#'.repeat(getHeaderLevel($content))} ${text}\n\n`;
}
if ($content.is('p')) { // paragraph
text = text + '\n\n';
}
if ($content.is('.ui.message')) { // quote
text = text.split('\n').map(s => s === '' ? '>' : '> ' + s).join('\n') + '\n';
// must one more \n, because we don't want two quote being merged into one
}
if ($content.is('a')) { // link
return `[${text}](${$content.attr('href')})`;
}
if ($content.is('img')) { // image
return `})`;
}
return text;
};
const createButton = function ($content) {
return $('<a>')
.attr('class', 'small ui primary button copyMD')
.text('copy MD')
.on('click', function () {
if ($(this).data('copy')) {
return;
}
const text = getText($content);
console.log(text);
// alert(text);
$(this).data('copy', true)
.text('copying');
copyText(text)
.then(() => {
$(this).text('copyied!');
})
.catch((err) => {
$(this).text(err || 'failed!');
})
.finally(() => {
setTimeout(() => {
$(this).data('copy', null)
.text('copy MD');
}, 3000);
});
});
};
if (window.location.href.includes('problem')) {
$('.ui.top.attached.header').each(function () {
$(this).append(createButton(
$(this).parent().children().last()
));
});
}
if (window.location.href.includes('article')) {
$('.padding>p:first').append(createButton(
$('#content')
));
$('.comment').each(function () {
$(this).find('.metadata').append(createButton(
$(this).find('.text')
))
});
}
if (window.location.href.includes('user')) {
const $column = $("div:has(>h4:contains('个性签名'))"); // cannot be absolutely positioned
$column.find('.header').append(createButton(
$column.find('.segment')
));
}
};
const markdownEditor = async function () {
$('.markdown-edit').each(function () {
const $original = $(this);
const initialVal = $original.val();
const $container = $('<div>').addClass('markdown-container');
const $left = $('<div>').addClass('markdown-left');
const $right = $('<div>').addClass('markdown-right');
const $textarea = $('<textarea class="markdown-source"></textarea>')
.val(initialVal).attr('name', $original.attr('name'))
.attr('id', $original.attr('id')); // fix article preview error
const $preview = $('<div>').addClass('markdown-preview');
const $toggleButton = $('<a>').attr('class', 'small ui primary button toggle-preview').text('切换预览');
$left.append($toggleButton, $textarea);
$right.append($preview);
$container.append($left, $right);
$original.replaceWith($container);
function update() {
$.post('/api/markdown', { s: $textarea.val() }, function (s) {
$preview.html(s);
});
}
$textarea.on('input', update);
update();
$toggleButton.on('click', function () {
if ($right.is(':visible')) {
$right.hide();
$left.addClass('markdown-full');
} else {
$right.show();
$left.removeClass('markdown-full');
}
});
});
};
const ranklistOverflowFix = async function () {
const $container = $('.main');
const $table = $('.table');
$container.css({
'padding-left': '10px',
'overflow-x': 'auto'
});
$table.css({
'transform-origin': 'left top'
});
const $button = $('<a>')
.css('vertical-align', 'bottom')
.attr('class', 'small ui primary button copyMD')
.appendTo($container.find('.header'));
$container.find('.header').prependTo(
$container.parent()
);
const ADJUST = 30;
const _wrapperWidth = $container.width();
const _tableWidth = $table.outerWidth() + ADJUST;
const _scale = _wrapperWidth / _tableWidth;
let flag = _scale < .9;
const layout = function () {
const wrapperWidth = $container.width();
const tableWidth = $table.outerWidth() + ADJUST;
const scale = wrapperWidth / tableWidth;
if (flag) {
$table.css('transform', 'scale(' + scale + ')');
$button.text('unscale');
} else {
$table.css('transform', '');
$button.text('scale');
}
};
layout();
$button.click(() => {
flag = !flag;
layout();
});
$(window).resize(layout);
};
const href = window.location.href;
if (typeof unsafeWindow.onEditorLoaded === 'function')
unsafeWindow.onEditorLoaded(problemEditor);
if (href.includes('submission'))
$(document).ready(submissionEditor);
if ($('.markdown-edit').length > 0)
$(document).ready(markdownEditor);
if (href.includes('ranklist'))
$(document).ready(ranklistOverflowFix);
$(document).ready(copyMarkdown);
})();
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/19024170。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。

浙公网安备 33010602011771号