';
}
}
$zip_file->close();
}
// Shared vanilla-JS modal helper. Emitted at most once per request, and it is
// also guarded in JS, so both the test-detail modal and the source-code modal
// can use it independently (a page with only one of them still works).
function echoUOJModalHelper() {
static $emitted = false;
if ($emitted) {
return;
}
$emitted = true;
echo <<<'UOJMODAL'
UOJMODAL;
}
class JudgementDetailsPrinter {
private $name;
private $styler;
private $dom;
private $subtask_num;
private $prefix;
private $blocks = array();
private $pending_tests = array();
private $bodies = array();
private $body_seq = 0;
private $rect_seq = 0;
private $auto_select_id = null;
private static $instance_seq = 0;
private function _print_c($node) {
foreach ($node->childNodes as $child) {
if ($child->nodeName == '#text') {
echo htmlspecialchars($child->nodeValue);
} else {
$this->_print($child);
}
}
}
private function _print($node) {
if ($node->nodeName == 'error') {
echo "
\n";
$this->_print_c($node);
echo "\n
";
} elseif ($node->nodeName == 'in') {
echo "
标准输入:
\n";
$this->_print_c($node);
echo "\n
";
} elseif ($node->nodeName == 'out') {
echo "
标准输出:
\n";
$this->_print_c($node);
echo "\n
";
} elseif ($node->nodeName == 'res') {
echo "
检验结果:
\n";
$this->_print_c($node);
echo "\n
";
} else {
echo '<', $node->nodeName;
foreach ($node->attributes as $attr) {
echo ' ', $attr->name, '="', htmlspecialchars($attr->value), '"';
}
echo '>';
$this->_print_c($node);
echo '', $node->nodeName, '>';
}
}
private function getVerdictAbbr($info) {
$info = trim($info);
$map = array(
'Accepted' => 'AC',
'Extra Test Passed' => 'AC',
'Success' => 'AC',
'Acceptable Answer' => 'AC',
'Wrong Answer' => 'WA',
'Time Limit Exceeded' => 'TLE',
'Memory Limit Exceeded' => 'MLE',
'Runtime Error' => 'RE',
'Output Limit Exceeded' => 'OLE',
'Dangerous Syscalls' => 'DGS',
'Judgement Failed' => 'JGF',
'No Comment' => '?',
);
if ($info === '') {
return '?';
}
if (isset($map[$info])) {
return $map[$info];
}
$words = preg_split('/\s+/', $info, -1, PREG_SPLIT_NO_EMPTY);
if (count($words) > 1) {
$abbr = '';
foreach ($words as $word) {
$abbr .= strtoupper(substr($word, 0, 1));
if (strlen($abbr) >= 4) {
break;
}
}
if ($abbr !== '') {
return substr($abbr, 0, 4);
}
}
if (function_exists('mb_substr')) {
return strtoupper(mb_substr($info, 0, 4, 'UTF-8'));
}
return strtoupper(substr($info, 0, 4));
}
private function getVerdictClass($info) {
$info = trim($info);
if ($info == 'Accepted' || $info == 'Extra Test Passed' || $info == 'Success' || $info == 'Acceptable Answer') {
return 'uoj-test-rect-ac';
}
if ($info == 'Wrong Answer') {
return 'uoj-test-rect-wa';
}
return 'uoj-test-rect-other';
}
private function collectBlocks($node) {
foreach ($node->childNodes as $child) {
if ($child->nodeType != XML_ELEMENT_NODE) {
continue;
}
if ($child->nodeName == 'subtask') {
$this->flushPendingTests();
$this->blocks[] = array('type' => 'subtask', 'node' => $child);
} elseif ($child->nodeName == 'test' || $child->nodeName == 'custom-test') {
$this->pending_tests[] = $child;
} elseif ($child->nodeName == 'error') {
$this->flushPendingTests();
$this->blocks[] = array('type' => 'error', 'node' => $child);
} else {
$this->collectBlocks($child);
}
}
}
private function flushPendingTests() {
if (count($this->pending_tests)) {
$this->blocks[] = array('type' => 'tests', 'nodes' => $this->pending_tests);
$this->pending_tests = array();
}
}
private function collectRoot($node) {
if ($node->nodeName == 'subtask') {
$this->blocks[] = array('type' => 'subtask', 'node' => $node);
} elseif ($node->nodeName == 'test' || $node->nodeName == 'custom-test') {
$this->pending_tests[] = $node;
} elseif ($node->nodeName == 'error') {
$this->blocks[] = array('type' => 'error', 'node' => $node);
} else {
$this->collectBlocks($node);
}
}
private function renderRect($node) {
$info = trim($node->getAttribute('info'));
$num = $node->getAttribute('num');
$time = $node->getAttribute('time');
$memory = $node->getAttribute('memory');
$this->rect_seq++;
if ($num !== '' && is_numeric($num)) {
$num_text = ((int)$num > 0) ? ('#' . (int)$num) : 'Extra';
} else {
$num_text = '#' . $this->rect_seq;
}
// The verdict abbreviation is always the label. UOJ stores a per-test
// point value in score="..." (e.g. 10 on every test of an ACM problem),
// so it is NOT a partial-credit signal and must never replace the
// verdict (see the subtask header for the per-subtask score instead).
$label = $this->getVerdictAbbr($info);
$meta = array();
if ($time !== '' && is_numeric($time) && $time >= 0) {
$meta[] = $time . 'ms';
}
if ($memory !== '' && is_numeric($memory) && $memory >= 0) {
$meta[] = $memory . 'kb';
}
$class = $this->getVerdictClass($info);
$title = (strlen($num_text) && $num_text[0] === '#') ? trim($num_text . ' ' . $info) : $info;
if ($this->styler->shouldFadeDetails($info)) {
echo '
';
foreach ($tests as $test) {
$this->renderRect($test);
}
echo '
';
echo '
';
}
private function renderGrid() {
echo '
';
// Only once per page: a two-phase submission (pretest + final test) runs
// this printer twice, and showing the same tip above both grids was
// confusing. static so it spans every instance in this request.
static $tip_shown = false;
if (!$tip_shown && $this->styler->show_small_tip) {
$tip_shown = true;
echo '