+
+
diff --git a/app/controllers/problem_statement_md.php b/app/controllers/problem_statement_md.php
new file mode 100644
index 0000000..843a99b
--- /dev/null
+++ b/app/controllers/problem_statement_md.php
@@ -0,0 +1,68 @@
+= $problem_content['statement_md'] ?>
diff --git a/app/controllers/problem_statistics.php b/app/controllers/problem_statistics.php
index 0d078b7..ad88c05 100644
--- a/app/controllers/problem_statistics.php
+++ b/app/controllers/problem_statistics.php
@@ -31,24 +31,24 @@ function scoreDistributionData() {
} else if ($row[0] == 100) {
$has_score_100 = true;
}
- $score = $row[0] * 100;
+ $score = round($row[0] * 1000000);
$data[] = array('score' => $score, 'count' => $row[1]);
}
if (!$has_score_0) {
array_unshift($data, array('score' => 0, 'count' => 0));
}
if (!$has_score_100) {
- $data[] = array('score' => 10000, 'count' => 0);
+ $data[] = array('score' => 100 * 1000000, 'count' => 0);
}
return $data;
}
$data = scoreDistributionData();
- $pre_data = $data;
- $suf_data = $data;
for ($i = 0; $i < count($data); $i++) {
- $data[$i]['score'] /= 100;
+ $data[$i]['score'] /= 1000000;
}
+ $pre_data = $data;
+ $suf_data = $data;
for ($i = 1; $i < count($data); $i++) {
$pre_data[$i]['count'] += $pre_data[$i - 1]['count'];
}
@@ -134,7 +134,7 @@ function scoreDistributionData() {
return (x.getTime() / 100).toString();
},
hoverCallback: function(index, options, content, row) {
- var scr = row.score / 100;
+ var scr = row.score;
return '
' + 'score: ≤' + scr + '
' +
'
';
},
@@ -161,7 +161,7 @@ function scoreDistributionData() {
return (x.getTime() / 100).toString();
},
hoverCallback: function(index, options, content, row) {
- var scr = row.score / 100;
+ var scr = row.score;
return '
' + 'score: ≥' + scr + '
' +
'
';
},
diff --git a/app/controllers/submissions_list.php b/app/controllers/submissions_list.php
index cc4f565..900480d 100644
--- a/app/controllers/submissions_list.php
+++ b/app/controllers/submissions_list.php
@@ -5,18 +5,14 @@
requirePHPLib('form');
- function validateTime($time) {
- return is_string($time) && (preg_match('/^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])( (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))?$/', $time));
- }
-
$search_form = new SOJForm();
$common = 'class="form-control input-sm" ';
$q_problem_id = $search_form->addText('problem_id', UOJLocale::get('problems::problem id').':', $common . 'maxlength="4" style="width:4em"', 'validateUInt');
$q_submitter = $search_form->addText('submitter', UOJLocale::get('username').':', $common . 'maxlength="20" style="width:10em"', 'validateUsername');
- $q_min_score = $search_form->addText('min_score', UOJLocale::get('score range').':', $common . 'maxlength="5" style="width:4em" placeholder="-∞"', 'validateInt');
- $q_max_score = $search_form->addText('max_score', '~', $common . 'maxlength="5" style="width:4em" placeholder="+∞"', 'validateInt');
+ $q_min_score = $search_form->addText('min_score', UOJLocale::get('score range').':', $common . 'maxlength="5" style="width:4em" placeholder="-∞"', 'validateReal');
+ $q_max_score = $search_form->addText('max_score', '~', $common . 'maxlength="5" style="width:4em" placeholder="+∞"', 'validateReal');
$q_language = $search_form->addText('language', UOJLocale::get('problems::language').':', $common . 'maxlength="10" style="width:8em"', validateLength(10));
- $q_cut_off_time = $search_form->addText('cut_off_time', UOJLocale::get('cut off time').':', $common . 'maxlength="20" style="width:14em; font-family: monospace" placeholder="YYYY-MM-DD[ hh:mm:ss]"', validateTime);
+ $q_cut_off_time = $search_form->addText('cut_off_time', UOJLocale::get('cut off time').':', $common . 'maxlength="20" style="width:14em; font-family: monospace" placeholder="YYYY-MM-DD[ hh:mm:ss]"', "validateTime");
$search_form->addSubmit(UOJLocale::get('search'));
$conds = array();
diff --git a/app/locale/basic/en.php b/app/locale/basic/en.php
index 0ef6edf..4ee35b0 100644
--- a/app/locale/basic/en.php
+++ b/app/locale/basic/en.php
@@ -173,4 +173,8 @@
'manageable' => 'Manageable',
'log out all' => 'Log out on all devices',
'cut off time' => 'Cut-off time',
+ 'problem collection' => 'Collection',
+ 'manage' => 'Manage',
+ 'propose' => 'Propose',
+ 'note' => 'Note',
];
diff --git a/app/locale/basic/zh-cn.php b/app/locale/basic/zh-cn.php
index 32137b6..32a2829 100644
--- a/app/locale/basic/zh-cn.php
+++ b/app/locale/basic/zh-cn.php
@@ -173,4 +173,8 @@
'manageable' => '有权限',
'log out all' => '在所有设备登出',
'cut off time' => '截止时间',
+ 'problem collection' => '题单',
+ 'manage' => '管理',
+ 'propose' => '提议',
+ 'note' => '备注',
];
diff --git a/app/models/UOJBlogEditor.php b/app/models/UOJBlogEditor.php
index c032032..bb3d8c5 100644
--- a/app/models/UOJBlogEditor.php
+++ b/app/models/UOJBlogEditor.php
@@ -41,6 +41,11 @@ function __construct() {
if (strlen($content_md) > 1000000) {
return '内容过长';
}
+ foreach (UOJConfig::$data['profile']['oj-address'] as $address) {
+ if (strpos($content_md, $address) !== false) {
+ return 'Warning: 使用相对链接';
+ }
+ }
return '';
},
'tags' => function(&$tags) {
@@ -78,10 +83,15 @@ public function validate($name) {
}
private function receivePostData() {
$errors = array();
+ $warnings = array();
foreach (array('title', 'content_md', 'tags') as $name) {
$cur_err = $this->validate($name);
if ($cur_err) {
- $errors[$name] = $cur_err;
+ if (substr($cur_err, 0, 7) == 'Warning') {
+ $warnings[$name] = $cur_err;
+ } else {
+ $errors[$name] = $cur_err;
+ }
}
}
if ($errors) {
@@ -188,11 +198,12 @@ private function receivePostData() {
}
$this->post_data['content'] = json_encode($config) . "\n" . $this->post_data['content'];
}
+ return $warnings;
}
public function handleSave() {
$save = $this->save;
- $this->receivePostData();
+ $warnings = $this->receivePostData();
$ret = $save($this->post_data);
if (!$ret) {
$ret = array();
@@ -218,6 +229,8 @@ public function handleSave() {
$ret['html'] = ob_get_contents();
ob_end_clean();
}
+
+ $ret['warnings'] = $warnings;
die(json_encode($ret));
}
diff --git a/app/route.php b/app/route.php
index 9d0d949..f631a39 100644
--- a/app/route.php
+++ b/app/route.php
@@ -17,11 +17,18 @@
Route::any('/problems/template', '/problem_set.php?tab=template');
Route::any('/problems/contested', '/problem_set.php?tab=contested');
Route::any('/problem/{id}', '/problem.php');
+ Route::any('/problem/{id}/statement.md', '/problem_statement_md.php');
Route::any('/problem/{id}/statistics', '/problem_statistics.php');
Route::any('/problem/{id}/manage/statement', '/problem_statement_manage.php');
Route::any('/problem/{id}/manage/managers', '/problem_managers_manage.php');
Route::any('/problem/{id}/manage/data', '/problem_data_manage.php');
+ Route::any('/collection', '/collection.php');
+ Route::any('/collection/propose', '/collection_propose.php');
+ Route::any('/collection/manage', '/collection_manage.php');
+ Route::any('/collection/{username}', '/collection_user.php');
+ Route::any('/collection/{username}/manage', '/collection_user_manage.php');
+
Route::any('/contests', '/contests.php');
Route::any('/contest/new', '/add_contest.php');
Route::any('/contest/{id}', '/contest_inside.php');
diff --git a/app/uoj-form-lib.php b/app/uoj-form-lib.php
index 7aaa827..0a43404 100644
--- a/app/uoj-form-lib.php
+++ b/app/uoj-form-lib.php
@@ -998,3 +998,7 @@ public function printHTML($url) {
function validateLength($len) {
return function($x) use ($len) {return mb_strlen($x) <= $len;};
}
+
+ function validateTime($time) {
+ return is_string($time) && (preg_match('/^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])( (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))?$/', $time));
+ }
diff --git a/app/uoj-html-lib.php b/app/uoj-html-lib.php
index c1c28c4..e4043f6 100644
--- a/app/uoj-html-lib.php
+++ b/app/uoj-html-lib.php
@@ -168,6 +168,8 @@ function getSubmissionJudgedStatusStr($result_error, $score, $uri = null) {
$html_type = 'span';
$head = $html_type;
}
+ $score = rtrim($score, '0');
+ $score = rtrim($score, '.');
if (isset($result_error))
return '<' . $head . " class=\"small\">$result_error" . $html_type . '>';
else
@@ -321,7 +323,7 @@ function echoSubmission($submission, $config, $user) {
$limitLevel = querySubmissionDetailPermission($user, $submission);
$problem = queryProblemBrief($submission['problem_id']);
$status = explode(', ', $submission['status'])[0];
- $show_status_details = isOurSubmission($user, $submission) && $status !== 'Judged';
+ $show_status_details = (isOurSubmission($user, $submission) || isSuperUser(Auth::user())) && $status !== 'Judged';
$submission_uri = getSubmissionUri($submission['id']);
$submission_id_str = getSubmissionLink($submission['id']);
@@ -1266,6 +1268,14 @@ private function _print($node) {
$this->_print_c($node);
echo '
';
+ } elseif ($node->nodeName == 'err') {
+ if(count($node->childNodes)) {
+ echo '
error (stderr):
+';
+ $this->_print_c($node);
+ echo '
+
';
+ }
} else {
echo '<', $node->nodeName;
foreach ($node->attributes as $attr) {
diff --git a/app/uoj-query-lib.php b/app/uoj-query-lib.php
index 47eb374..c5b9753 100644
--- a/app/uoj-query-lib.php
+++ b/app/uoj-query-lib.php
@@ -94,6 +94,18 @@ function queryProblemTags($id) {
return $tags;
}
+function queryCollectionTags($id) {
+ $result = DB::select("select tag from collection_tags where collection_id = '$id' order by id");
+ for ($tags = array(); $row = DB::fetch($result, MYSQLI_NUM); $tags[] = $row[0]);
+ return $tags;
+}
+
+function queryUserCollectionTags($owner, $id) {
+ $result = DB::select("select tag from collection_user_tags where collection_id = '$id' and owner='$owner' order by id");
+ for ($tags = array(); $row = DB::fetch($result, MYSQLI_NUM); $tags[] = $row[0]);
+ return $tags;
+}
+
function queryContestProblemRank($contest, $problem) {
if (!DB::selectFirst("select * from contests_problems where contest_id = {$contest['id']} and problem_id = {$problem['id']}")) {
return null;
@@ -405,12 +417,14 @@ function getHacksAuditLog($config = array()) {
'actor' => $his['hacker'],
'details' => $his
);
- $audit_log[] = array(
- 'time' => $his['judge_time'],
- 'type' => 'hack judgement, auto',
- 'hack_id' => $his['hack_id'],
- 'details' => $his
- );
+ if($his['success'] != null) {
+ $audit_log[] = array(
+ 'time' => $his['judge_time'],
+ 'type' => 'hack judgement, auto',
+ 'hack_id' => $his['hack_id'],
+ 'details' => $his
+ );
+ }
}
sortAuditLogByTime($audit_log);
return $audit_log;
diff --git a/app/uoj-validate-lib.php b/app/uoj-validate-lib.php
index 17eadb5..4cbff04 100644
--- a/app/uoj-validate-lib.php
+++ b/app/uoj-validate-lib.php
@@ -32,6 +32,14 @@ function validateInt($x) {
return is_string($x) and validateUInt($x[0] === '-' ? substr($x, 1) : $x);
}
+function validateUReal($x) {
+ return is_string($x) and preg_match('/^(0|[1-9][0-9]{0,8})(\.[0-9]{0,6})?$/', $x);
+}
+
+function validateReal($x) {
+ return is_string($x) and validateUReal($x[0] === '-' ? substr($x, 1) : $x);
+}
+
function validateUploadedFile($name) {
return isset($_FILES[$name]) && is_uploaded_file($_FILES[$name]['tmp_name']);
}
diff --git a/app/views/blog-preview.php b/app/views/blog-preview.php
index fc426e7..6e36cf1 100644
--- a/app/views/blog-preview.php
+++ b/app/views/blog-preview.php
@@ -23,6 +23,17 @@
= $content ?>
+
diff --git a/css/soj-theme.css b/css/soj-theme.css
index 0ff2a2a..6c9ad3f 100644
--- a/css/soj-theme.css
+++ b/css/soj-theme.css
@@ -401,6 +401,10 @@ pre {
margin-left: 5px;
margin-right: 5px;
}
+.uoj-collection-tag {
+ margin-left: 5px;
+ margin-right: 5px;
+}
.uoj-blog-tag {
margin-left: 5px;
margin-right: 5px;
diff --git a/js/blog-editor/blog-editor.js b/js/blog-editor/blog-editor.js
index 747fa52..310d0ed 100644
--- a/js/blog-editor/blog-editor.js
+++ b/js/blog-editor/blog-editor.js
@@ -184,6 +184,9 @@ function blog_editor_init(name, editor_config) {
$(['title', 'content_md', 'tags']).each(function() {
ok &= showErrorHelp(name + '_' + this, data[this]);
});
+ $(['title', 'content_md', 'tags']).each(function() {
+ showWarningHelp(name + '_' + this, data['warnings'][this]);
+ });
if (data.extra !== undefined) {
alert(data.extra);
ok = false;
diff --git a/js/marked.js b/js/marked.js
index f2c231e..0c29643 100644
--- a/js/marked.js
+++ b/js/marked.js
@@ -1,5 +1,5 @@
/**
- * marked - a markdown parser
+ * Modified from marked - a markdown parser
* Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
* https://github.com/chjj/marked
*/
@@ -363,7 +363,8 @@ Lexer.prototype.token = function(src, top) {
src = src.substring(cap[0].length);
this.tokens.links[cap[1].toLowerCase()] = {
href: cap[2],
- title: cap[3]
+ title: cap[3],
+ rest: ''
};
continue;
}
@@ -452,11 +453,14 @@ var inline = {
math: /^\$((?:[^\\]|\\\\|\\[^\\]+?)+?)\$|^\$\$((?:[^\\]|\\\\|\\[^\\]+?)+?)\$\$|^\\begin{[^}]+}((?:[^\\]|\\\\|\\[^\\]+?)+?)\\end{[^}]+}/,
br: /^ {2,}\n(?!\s*$)/,
del: noop,
- text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+inline._href = /\s*([^\s)]*?)>?(?:\s+['"]([^)]*?)['"])?(\s*(?:\s\.[\w=]+\s*)*)/;
inline.link = replace(inline.link)
('inside', inline._inside)
@@ -618,7 +622,8 @@ InlineLexer.prototype.output = function(src) {
src = src.substring(cap[0].length);
out += this.outputLink(cap, {
href: cap[2],
- title: cap[3]
+ title: cap[3],
+ rest: cap[4]
});
continue;
}
@@ -703,6 +708,31 @@ InlineLexer.prototype.output = function(src) {
InlineLexer.prototype.outputLink = function(cap, link) {
if (cap[0][0] !== '!') {
+
+ if (link.href[0] == '%') {
+ link.href = link.href.substring(1);
+ return '
'
+ + this.output(cap[1])
+ + '';
+ }
+
+ if (link.href[0] == ':') {
+ var ret = this.extractProperties(link.rest);
+ if (link.href == ':user' && ret[1].rating !== undefined) {
+ return '
' + escape(cap[1]) + '';
+ } else {
+ return '
{errors occur in processing ' + escape(link.href) + '}';
+ }
+ }
+
return '
';
} else {
+ var ret = this.extractProperties(link.rest)
+ , style = ''
+ , cls;
+
+ if (ret[1].h !== undefined) {
+ style += "height:" + ret[1].h + "px;";
+ }
+ if (ret[1].w !== undefined) {
+ style += "width:" + ret[1].w + "px;";
+ }
+ if (ret[1].left) {
+ cls = 'img-responsive';
+ } else if (ret[1].right) {
+ cls = 'img-responsive';
+ style += "margin-left: auto;";
+ } else if (ret[1].inline) {
+ cls = '';
+ } else {
+ cls = 'img-responsive center-block';
+ }
+
return '
';
}
};
+/**
+ * Extract Properties
+ */
+
+InlineLexer.prototype.extractProperties = function(src) {
+ var props = {}
+ , cap;
+
+ if (!src) {
+ return ['', props];
+ }
+ while (src) {
+ // width, height, rating
+ if (cap = this.rules.property_num.exec(src)) {
+ src = src.substring(0, src.length - cap[0].length);
+ if (!(cap[1] in props)) {
+ props[cap[1]] = cap[2];
+ }
+ continue;
+ }
+
+ // keep, left, right, center
+ if (cap = this.rules.property_bool.exec(src)) {
+ src = src.substring(0, src.length - cap[0].length);
+ props[cap[1]] = true;
+ continue;
+ }
+
+ break;
+ }
+
+ return [src, props];
+};
+
/**
* Mangle Links
*/
@@ -820,8 +903,8 @@ Parser.prototype.parseText = function() {
*/
Parser.prototype.tok = function() {
- var tok_class = this.options.getElementClass == null ? null : this.options.getElementClass(this.token);
- tok_class = tok_class == null ? '' : ' class="' + tok_class + '"';
+ var inner_tok_class = this.options.getElementClass == null ? null : this.options.getElementClass(this.token);
+ tok_class = inner_tok_class == null ? '' : ' class="' + inner_tok_class + '"';
switch (this.token.type) {
case 'space': {
return '';
@@ -851,16 +934,18 @@ Parser.prototype.tok = function() {
if (!this.token.escaped) {
this.token.text = escape(this.token.text, true);
}
-
- return '' + this.token.text + '
';
+ } else if (this.options.getLangClass && this.token.lang != undefined) {
+ return ''
+ + '">'
+ this.token.text
+ '
\n';
+ } else {
+ return '' + this.token.text + '
';
+ }
}
case 'table': {
var body = ''
@@ -868,15 +953,32 @@ Parser.prototype.tok = function() {
, i
, row
, cell
+ , attr
, j;
+ var rowspan = false;
+
// header
body += '\n\n';
for (i = 0; i < this.token.header.length; i++) {
- heading = this.inline.output(this.token.header[i]);
- body += this.token.align[i]
- ? '| ' + heading + ' | \n'
- : '' + heading + ' | \n';
+ if (!this.token.header[i]) {
+ continue;
+ }
+
+ heading = this.inline.extractProperties(this.token.header[i]);
+ heading[0] = this.inline.output(heading[0]);
+ attr = '';
+ if (this.token.align[i]) {
+ attr += ' style="text-align:' + this.token.align[i] + '"';
+ }
+ if (heading[1].w !== undefined) {
+ attr += ' colspan="' + cell[1].w + '"';
+ }
+ if (heading[1].h !== undefined) {
+ attr += ' rowspan="' + cell[1].h + '"';
+ rowspan = true;
+ }
+ body += '' + heading[0] + ' | \n';
}
body += '
\n\n';
@@ -886,18 +988,39 @@ Parser.prototype.tok = function() {
row = this.token.cells[i];
body += '\n';
for (j = 0; j < row.length; j++) {
- cell = this.inline.output(row[j]);
- body += this.token.align[j]
- ? '| ' + cell + ' | \n'
- : '' + cell + ' | \n';
+ if (!row[j]) {
+ continue;
+ }
+
+ cell = this.inline.extractProperties(row[j]);
+ cell[0] = this.inline.output(cell[0]);
+ attr = '';
+ if (this.token.align[j]) {
+ attr += ' style="text-align:' + this.token.align[j] + '"';
+ }
+ if (cell[1].w !== undefined) {
+ attr += ' colspan="' + cell[1].w + '"';
+ }
+ if (cell[1].h !== undefined) {
+ attr += ' rowspan="' + cell[1].h + '"';
+ rowspan = true;
+ }
+ body += '' + cell[0] + ' | \n';
}
body += '
\n';
}
body += '\n';
- return '\n'
+ if (!rowspan) {
+ if (inner_tok_class != null) {
+ inner_tok_class += ' table-striped';
+ }
+ tok_class = inner_tok_class == null ? '' : ' class="' + inner_tok_class + '"';
+ }
+
+ return '
\n
\n';
}
case 'blockquote_start': {
var body = '';
@@ -953,6 +1076,9 @@ Parser.prototype.tok = function() {
}
case 'html': {
return this.token.text;
+ /*return !this.token.pre && !this.options.pedantic
+ ? this.inline.output(this.token.text)
+ : this.token.text;*/
}
case 'paragraph': {
return ''
@@ -1051,18 +1177,21 @@ marked.defaults = {
silent: false,
highlight: null,
getLangClass: function(lang) {
- lang = lang.toLowerCase();
- switch (lang) {
- case 'c': return 'sh_c';
- case 'c++': return 'sh_cpp';
- case 'pascal': return 'sh_pascal';
- default: return 'sh_' + lang;
- }
+ lang = lang.toLowerCase();
+ switch (lang) {
+ case 'c': return 'sh_c';
+ case 'c++': return 'sh_cpp';
+ case 'pascal': return 'sh_pascal';
+ default: return 'sh_' + lang;
+ }
},
- getElementClass: function (token) {
- if (token.type === "table")
- return "table table-bordered table-hover table-striped table-text-center";
- return null;
+ getElementClass: function(token) {
+ switch (token.type) {
+ case 'table':
+ return 'table table-bordered table-text-center table-vertical-middle';
+ default:
+ return null;
+ }
}
};
diff --git a/js/soj.js b/js/soj.js
index a0af683..ac58982 100644
--- a/js/soj.js
+++ b/js/soj.js
@@ -183,7 +183,7 @@ function getColOfScore(score) {
} else if (score == 100) {
return ColorConverter.toStr(ColorConverter.toRGB(new HSV(120, 100, 80)));
} else {
- return ColorConverter.toStr(ColorConverter.toRGB(new HSV(30 + score * 0.6, 100, 90)));
+ return ColorConverter.toStr(ColorConverter.toRGB(new HSV(30 + score * 0.6, 100, 90 - Math.max(Math.min(score - 30, 90 - score), 0) * 0.5)));
}
}
@@ -284,6 +284,16 @@ function showErrorHelp(name, err) {
}
}
+function showWarningHelp(name, err) {
+ if (err) {
+ $('#div-' + name).addClass('has-warning');
+ $('#help-' + name).text(err);
+ } else {
+ $('#div-' + name).removeClass('has-warning');
+ $('#help-' + name).text('');
+ }
+}
+
function getFormErrorAndShowHelp(name, val) {
var err = val($('#input-' + name).val());
return showErrorHelp(name, err);
@@ -385,6 +395,15 @@ $.fn.uoj_blog_tag = function() {
});
}
+$.fn.uoj_collection_tag = function() {
+ return this.each(function() {
+ var owner = $(this).data('owner');
+ var path_owner = '';
+ if(owner) path_owner = '/' + owner;
+ $(this).attr('href', '/collection' + path_owner + '?type=' + encodeURIComponent($(this).text()));
+ });
+}
+
// click zan
function click_zan(zan_id, zan_type, zan_delta, node) {
var loading_node = $('
loading...
');
@@ -451,8 +470,21 @@ function getCountdownStr(t) {
var mm = toFilledStr(x % 60, '0', 2);
x = Math.floor(x / 60);
var hh = x.toString();
+ if (x >= 24) {
+ hh = toFilledStr(x % 24, '0', 2);
+ }
+ x = Math.floor(x / 24);
+ var dd = x.toString();
var res = '';
+ if (x > 0) {
+ res += '' + dd + '';
+ res += ' day';
+ if (dd > 1) {
+ res += 's';
+ }
+ res += ' + ';
+ }
res += '' + hh + '';
res += ':';
res += '' + mm + '';
@@ -519,7 +551,7 @@ $.fn.uoj_highlight = function() {
$(this).find("span.uoj-groupname").each(replaceWithHighlightUsername);
$(this).find(".uoj-honor").uoj_honor();
$(this).find(".uoj-score").each(function() {
- var score_str = $(this).text(), score = parseInt(score_str), maxscore = parseInt($(this).data('max'));
+ var score_str = $(this).text(), score = parseFloat(score_str), maxscore = parseFloat($(this).data('max'));
if (score_str === '?') $(this).css("color", "rgb(0, 204, 204)");
else if (score_str.startsWith('+')) $(this).css("color", getColOfScore(100));
else if (score_str.startsWith('-')) $(this).css("color", getColOfScore(0));
@@ -539,6 +571,7 @@ $.fn.uoj_highlight = function() {
});
$(this).find(".uoj-problem-tag").uoj_problem_tag();
$(this).find(".uoj-blog-tag").uoj_blog_tag();
+ $(this).find(".uoj-collection-tag").uoj_collection_tag();
$(this).find(".uoj-click-zan-block").click_zan_block();
$(this).find(".countdown").countdown();
$(this).find(".uoj-readmore").readmore({
diff --git a/js/soj_collection_id2url.js b/js/soj_collection_id2url.js
new file mode 100644
index 0000000..d4e503b
--- /dev/null
+++ b/js/soj_collection_id2url.js
@@ -0,0 +1,90 @@
+document.getElementById('input-id').addEventListener('input', function(e) {
+ const input = e.target.value.trim();
+ const outputElement = document.getElementById('input-url');
+
+ if (!input) {
+ outputElement.value = '';
+ return;
+ }
+
+ // 1. Codeforces 常规题目 (cf1a 或 1a)
+ if (input.startsWith('cf') || /^\d{1,5}[a-z]\d{0,1}$/.test(input)) {
+ let problemId = input.startsWith('cf') ? input.substring(2) : input;
+
+ const cfMatch = problemId.match(/^(\d{1,5})([a-z]\d{0,1})$/);
+ if (cfMatch) {
+ const contestId = cfMatch[1];
+ const problemLetter = cfMatch[2].toUpperCase(); // 注意:大写字母
+ outputElement.value = `https://codeforces.com/problemset/problem/${contestId}/${problemLetter}`;
+ return;
+ }
+ }
+
+ // 2. AtCoder 格式 (abc/arc/agc + 数字 + 字母)
+ const atcoderMatch = input.match(/^(a[brg]c)(\d+)([a-z])$/i);
+ if (atcoderMatch) {
+ const contestType = atcoderMatch[1].toLowerCase();
+ const contestNumber = atcoderMatch[2];
+ const problemLetter = atcoderMatch[3].toLowerCase();
+ const contestId = contestType + contestNumber;
+
+ outputElement.value = `https://atcoder.jp/contests/${contestId}/tasks/${contestId}_${problemLetter}`;
+ return;
+ }
+
+ // 3. UOJ 格式 (uoj78)
+ const uojMatch = input.match(/^uoj(\d+)$/i);
+ if (uojMatch) {
+ const problemId = uojMatch[1];
+ outputElement.value = `https://uoj.ac/problem/${problemId}`;
+ return;
+ }
+
+ // 4. QOJ 格式 (qoj789)
+ const qojMatch = input.match(/^qoj(\d+)$/i);
+ if (qojMatch) {
+ const problemId = qojMatch[1];
+ outputElement.value = `https://qoj.ac/problem/${problemId}`;
+ return;
+ }
+
+ // 5. LOJ 格式 (loj456)
+ const lojMatch = input.match(/^loj(\d+)$/i);
+ if (lojMatch) {
+ const problemId = lojMatch[1];
+ outputElement.value = `https://loj.ac/p/${problemId}`;
+ return;
+ }
+
+ // 6. SOJ 格式 (soj567) - 相对路径
+ const sojMatch = input.match(/^soj(\d+)$/i);
+ if (sojMatch) {
+ const problemId = sojMatch[1];
+ outputElement.value = `/problem/${problemId}`;
+ return;
+ }
+
+ // 7. 洛谷格式 (lg1001 或 p1001)
+ const lgMatch = input.match(/^(?:lg|p)(\d+)$/i);
+ if (lgMatch) {
+ const problemId = lgMatch[1];
+ outputElement.value = `https://www.luogu.com.cn/problem/P${problemId}`;
+ return;
+ }
+
+ // 8. Codeforces Gym 题目 (cf123456a 或 123456a,6位以上数字)
+ if (input.startsWith('cf') || /^\d{6,}[a-z]$/.test(input)) {
+ let problemId = input.startsWith('cf') ? input.substring(2) : input;
+
+ const cfGymMatch = problemId.match(/^(\d{6,})([a-z])$/);
+ if (cfGymMatch) {
+ const contestId = cfGymMatch[1];
+ const problemLetter = cfGymMatch[2].toUpperCase(); // 注意:大写字母
+ outputElement.value = `https://codeforces.com/problemset/gymProblem/${contestId}/${problemLetter}`;
+ return;
+ }
+ }
+
+ // 如果不是已知格式,不改变输出
+ // outputElement.value = '';
+});