Compare commits

...

5 Commits

Author SHA1 Message Date
34270c328a
feat: dark mode (#43)
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-23 20:40:51 +08:00
a811b3e646
fix: dark mode bs5 table class 2023-02-23 20:34:49 +08:00
e542600dfa
feat: dark mode username color 2023-02-23 20:22:49 +08:00
87e0fd62f5
refactor: username color 2023-02-23 19:46:33 +08:00
b98edf87b0
feat: dark mode 2023-02-23 18:28:20 +08:00
21 changed files with 2499 additions and 420 deletions

View File

@ -35,7 +35,7 @@ foreach ($problem_configure->problem_conf->conf as $key => $val) {
<div class="card-header fw-bold">problem.conf 预览</div>
<div class="card-body p-0" id="problem-conf-preview">
<pre class="bg-light mb-0 p-3"><code><?= $problem_conf_str ?></code></pre>
<pre class="bg-body-tertiary mb-0 p-3"><code><?= $problem_conf_str ?></code></pre>
</div>
<div class="card-footer bg-transparent small text-muted">

View File

@ -50,7 +50,7 @@ function echoFilePre($file_name) {
echo '<h5 class="mb-1">', htmlspecialchars($file_name), '</h5>';
echo '<div class="text-muted small mb-1 font-monospace">', $mimetype, '</div>';
echo '<pre class="bg-light rounded uoj-pre">', "\n";
echo '<pre class="bg-body-tertiary rounded uoj-pre">', "\n";
$output_limit = 1000;
if (strStartWith($mimetype, 'text/')) {
@ -124,7 +124,7 @@ $info_form->appendHTML(<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">提交文件配置</label>
<div class="col-sm-9">
<pre class="uoj-pre bg-light rounded">
<pre class="uoj-pre bg-body-tertiary rounded">
$esc_submission_requirement
</pre>
</div>
@ -135,7 +135,7 @@ $info_form->appendHTML(<<<EOD
<div class="form-group row">
<label class="col-sm-3 control-label">其它配置</label>
<div class="col-sm-9">
<pre class="uoj-pre bg-light rounded">
<pre class="uoj-pre bg-body-tertiary rounded">
$esc_extra_config
</pre>
</div>

View File

@ -77,7 +77,7 @@ EOD;
die();
};
$new_problem_form->config['submit_container']['class'] = '';
$new_problem_form->config['submit_button']['class'] = 'bg-transparent border-0 d-block w-100 px-3 py-2 text-start';
$new_problem_form->config['submit_button']['class'] = 'bg-transparent text-body border-0 d-block w-100 px-3 py-2 text-start';
$new_problem_form->config['submit_button']['text'] = '<i class="bi bi-plus-lg"></i> 新建本地题目';
$new_problem_form->config['confirm']['text'] = '你真的要添加新题吗?';
$new_problem_form->runAtServer();

View File

@ -174,7 +174,7 @@ if (UOJUserBlog::userHasManagePermission(Auth::user())) {
]);
$hide_form->addInput('comment_hide_reason', [
'div_class' => 'mt-3',
'label' => '自定义隐藏理由',
'label' => '自定义隐藏理由(当上方隐藏理由为自定义时有效)',
'default_value' => '该评论由于违反社区规定,已被管理员隐藏',
'validator_php' => 'validateString',
]);
@ -327,9 +327,10 @@ $comments_pag = new Paginator([
<script>
$('.uoj-blog-hide-comment-btn').each(function() {
$(this).click(function() {
$(this).click(function(event) {
var comment_id = $(this).data('comment-id');
event.preventDefault();
toggleModalHideComment(comment_id, $('#comment-content-' + comment_id).html());
});
})

View File

@ -299,7 +299,7 @@ if ($cur_tab == 'profile') {
$update_profile_form->appendHTML(<<<EOD
<div class="mb-3">
<label for="input-username_color" class="form-label">用户名颜色</label>
<input type="text" class="form-control" id="input-username_color" aria-describedby="help-username_color" value="棕色 - #996600" disabled>
<input type="text" class="form-control" id="input-username_color" aria-describedby="help-username_color" value="棕色" disabled>
<div id="help-username_color" class="form-text">被封禁的用户无法修改用户名颜色。</div>
</div>
EOD);
@ -307,29 +307,22 @@ if ($cur_tab == 'profile') {
$update_profile_form->appendHTML(<<<EOD
<div class="mb-3">
<label for="input-username_color" class="form-label">用户名颜色</label>
<input type="text" class="form-control" id="input-username_color" aria-describedby="help-username_color" value="灰色 - #707070" disabled>
<input type="text" class="form-control" id="input-username_color" aria-describedby="help-username_color" value="灰色" disabled>
<div id="help-username_color" class="form-text">临时用户无法修改用户名颜色。</div>
</div>
EOD);
} else {
$additional_colors = [];
$username_colors = UOJUser::USERNAME_COLORS['user'];
if (isSuperUser($user)) {
$additional_colors['#9d3dcf'] = '紫色 - #9d3dcf';
$username_colors += UOJUser::USERNAME_COLORS['admin'];
}
$update_profile_form->addSelect('username_color', [
'div_class' => 'mb-3',
'label' => '用户名颜色',
'default_value' => $extra['username_color'],
'options' => $additional_colors + [
'#0d6efd' => '蓝色 - #0d6efd',
'#2da44e' => '绿色 - #2da44e',
'#e85aad' => '粉色 - #e85aad',
'#f32a38' => '红色 - #f32a38',
'#f57c00' => '橙色 - #f57c00',
'#00acc1' => '青色 - #00acc1',
],
'options' => $username_colors,
]);
}
$update_profile_form->handle = function (&$vdata) use ($user, $extra) {

View File

@ -355,7 +355,7 @@ function echoSubmissionContent($submission, $requirement) {
echo '<h4 class="card-title">' . $req['name'] . '</h4>';
echo '</div>';
echo '<div class="card-body">';
echo '<pre><code class="' . $sh_class . ' bg-light rounded p-3">' . $file_content . "\n" . '</code></pre>';
echo '<pre><code class="' . $sh_class . ' bg-body-tertiary rounded p-3">' . $file_content . "\n" . '</code></pre>';
echo '</div>';
echo '<div class="card-footer">' . $footer_text . '</div>';
echo '</div>';
@ -369,7 +369,7 @@ function echoSubmissionContent($submission, $requirement) {
echo '<h4 class="card-title">' . $req['file_name'] . '</h4>';
echo '</div>';
echo '<div class="card-body">';
echo '<pre class="bg-light rounded p-3 ">', "\n" . $file_content . "\n" . '</pre>';
echo '<pre class="bg-body-tertiary rounded p-3 ">', "\n" . $file_content . "\n" . '</pre>';
echo '</div>';
echo '<div class="card-footer">' . $footer_text . '</div>';
echo '</div>';
@ -406,7 +406,7 @@ class JudgmentDetailsPrinter {
}
private function _print(DOMElement $node) {
if ($node->nodeName == 'error') {
echo '<pre class="bg-light rounded p-3">', "\n";
echo '<pre class="bg-body-tertiary rounded p-3">', "\n";
$this->_print_c($node);
echo "\n</pre>";
} elseif ($node->nodeName == 'remote-result-container') {
@ -432,7 +432,7 @@ class JudgmentDetailsPrinter {
$language = $node->getAttribute("language");
echo '<div class="border-bottom p-3 copy-button-container">';
echo '<div class="fw-bold mb-2">源代码</div>';
echo '<pre class="mb-0"><code class="language-', $language, ' bg-light rounded p-3">';
echo '<pre class="mb-0"><code class="language-', $language, ' bg-body-tertiary rounded p-3">';
$this->_print_c($node);
echo "</code></pre>";
echo '</div>';
@ -662,22 +662,22 @@ class JudgmentDetailsPrinter {
}
} elseif ($node->nodeName == 'in') {
echo '<h4 class="fs-6 fw-bold">输入文件</h4>';
echo '<pre class="bg-light p-3 rounded">', "\n";
echo '<pre class="bg-body-tertiary p-3 rounded">', "\n";
$this->_print_c($node);
echo "\n</pre>";
} elseif ($node->nodeName == 'out') {
echo '<h4 class="fs-6 fw-bold">程序输出</h4>';
echo '<pre class="bg-light p-3 rounded">', "\n";
echo '<pre class="bg-body-tertiary p-3 rounded">', "\n";
$this->_print_c($node);
echo "\n</pre>";
} elseif ($node->nodeName == 'ans') {
echo '<h4 class="fs-6 fw-bold">答案文件</h4>';
echo '<pre class="bg-light p-3 rounded">', "\n";
echo '<pre class="bg-body-tertiary p-3 rounded">', "\n";
$this->_print_c($node);
echo "\n</pre>";
} elseif ($node->nodeName == 'res') {
echo '<h4 class="fs-6 fw-bold"><span>检查器信息</span></h4>';
echo '<pre class="bg-light p-3 rounded">', "\n";
echo '<pre class="bg-body-tertiary p-3 rounded">', "\n";
if ($node->hasChildNodes()) {
$this->_print_c($node);
} else {
@ -698,7 +698,7 @@ class JudgmentDetailsPrinter {
}
echo '<h4 class="mb-2">', $node->getAttribute("title"), ":</h4>";
}
echo '<pre class="bg-light p-3 rounded">', "\n";
echo '<pre class="bg-body-tertiary p-3 rounded">', "\n";
$this->_print_c($node);
echo "\n</pre>";
echo '</div>';

View File

@ -1,4 +1,29 @@
<?php
class CustomClassDef extends HTMLPurifier_AttrDef {
private $classes, $prefixes;
public function __construct($classes, $prefixes) {
$this->classes = $classes;
$this->prefixes = is_array($prefixes) ? join('|', $prefixes) : $prefixes;
}
public function validate($string, $config, $context) {
$classes = preg_split('/\s+/', $string);
$valid_classes = [];
foreach ($classes as $class) {
if (
in_array($class, $this->classes) ||
preg_match("/^({$this->prefixes})/i", $class)
) {
$valid_classes[] = $class;
}
}
return join(' ', $valid_classes);
}
}
class HTML {
public static function escape(?string $str, $cfg = []) {
@ -454,7 +479,7 @@ class HTML {
'data-src' => 'URI',
],
'span' => [
'class' => 'Enum#uoj-username',
'class' => new CustomClassDef(['uoj-username'], ['uoj-username-']),
'data-realname' => 'Text',
'data-color' => 'Color',
],
@ -485,7 +510,7 @@ class HTML {
'del' => [],
'br' => [],
'span' => [
'class' => 'Enum#uoj-username',
'class' => new CustomClassDef(['uoj-username'], ['uoj-username-']),
'data-realname' => 'Text',
'data-color' => 'Color',
],

View File

@ -73,7 +73,7 @@ class UOJProblemDataDisplayer {
echo '<h5 class="mb-1">', HTML::escape($file_name), '</h5>';
echo '<div class="text-muted small mb-1 font-monospace">', $mimetype, '</div>';
echo '<pre class="bg-light rounded uoj-pre">', "\n";
echo '<pre class="bg-body-tertiary rounded uoj-pre">', "\n";
$type = $mimetype == 'binary' ? 'binary' : 'text';
echo HTML::escape(uojStringPreview($content, $max_len, $type));
echo "\n</pre>";

View File

@ -218,7 +218,7 @@ trait UOJSubmissionLikeTrait {
{$req['name']}
</div>
<div class="{$card_body_class} copy-button-container">
<pre class="mb-0"><code class="$sh_class bg-light rounded p-3">{$file_content}\n</code></pre>
<pre class="mb-0"><code class="$sh_class bg-body-tertiary rounded p-3">{$file_content}\n</code></pre>
</div>
<div class="{$card_footer_class}">$footer_text</div>
</div>
@ -238,7 +238,7 @@ trait UOJSubmissionLikeTrait {
{$req['file_name']}
</div>
<div class="{$card_body_class}">
<pre class="mb-0 bg-light rounded p-3">\n{$file_content}\n</pre>
<pre class="mb-0 bg-body-tertiary rounded p-3">\n{$file_content}\n</pre>
</div>
<div class="{$card_footer_class}">{$footer_text}</div>
</div>

View File

@ -11,6 +11,21 @@ class UOJUser {
const MAX_UA_LEN = 300;
const MAX_HISTORY_LEN = 20;
// Don't forget to change values in `models/HTML.php` and `css/uoj-bs5.css`
const USERNAME_COLORS = [
'user' => [
'blue' => 'Blue',
'green' => 'Green',
'pink' => 'Pink',
'red' => 'Red',
'orange' => 'Orange',
'cyan' => 'Cyan',
],
'admin' => [
'purple' => 'Purple',
],
];
public static $visibility_codes = [
'all' => [
'html' => '',
@ -277,23 +292,23 @@ class UOJUser {
public static function getUserColor2($usergroup, $custom_color = null) {
if ($usergroup == 'B') {
return '#996600';
return 'brown';
}
if ($usergroup == 'T') {
return '#707070';
return 'gray';
}
if ($usergroup == 'S') {
return $custom_color ?: '#9d3dcf';
return $custom_color ?: 'purple';
}
// 前管理员设置颜色为紫色的,颜色改为蓝色
if ($custom_color == '#9d3dcf') {
return '#0d6efd';
if ($custom_color == 'purple') {
return 'blue';
}
return $custom_color ?: '#0d6efd';
return $custom_color ?: 'blue';
}
public static function getLink($user, $cfg = []) {
@ -318,10 +333,12 @@ class UOJUser {
$realname = '';
}
$color = $cfg['color'] ? UOJUser::getUserColor($user) : '';
return HTML::tag('span', [
'class' => 'uoj-username',
'data-color' => $cfg['color'] ? UOJUser::getUserColor($user) : '',
'class' => "uoj-username uoj-username-{$color}",
'data-realname' => trim(HTML::escape($realname)),
'data-color' => $color,
], $user['username']);
}

View File

@ -0,0 +1,60 @@
<?php
return function ($type) {
if ($type == 'up') {
DB::init();
function getColorName($color) {
switch ($color) {
case '#0d6efd':
case 'blue':
return 'blue';
case '#2da44e':
case 'green':
return 'green';
case '#e85aad':
case 'pink':
return 'pink';
case '#f32a38':
case 'red':
return 'red';
case '#f57c00':
case 'orange':
return 'orange';
case '#00acc1':
case 'cyan':
return 'cyan';
case '#9d3dcf':
case 'purple':
return 'purple';
case '#707070':
case 'gray':
return 'gray';
case '#996600':
case 'brown':
return 'brown';
default:
return 'blue';
}
}
$users = DB::selectAll("select * from user_info");
foreach ($users as $user) {
$extra = UOJUser::getExtra($user);
$original_color = $extra['username_color'];
$new_color = getColorName($original_color);
$extra['username_color'] = $new_color;
DB::update([
"update user_info",
"set", [
"extra" => json_encode($extra)
],
"where", [
"username" => $user['username']
],
]);
echo "{$user['username']}: {$original_color} -> {$new_color}\n";
}
}
};

View File

@ -1,4 +1,4 @@
<div class="navbar navbar-light navbar-expand-md bg-white shadow-sm mb-4" role="navigation">
<div class="navbar navbar-light navbar-expand-md bg-body shadow-sm mb-4" role="navigation">
<div class="container">
@ -41,98 +41,11 @@
<hr class="d-md-none text-muted">
<ul class="nav navbar-nav ms-md-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-translate"></i>
<?= UOJLocale::get('_common_name') ?>
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="<?= HTML::url(UOJContext::requestURI(), array('params' => array('locale' => 'zh-cn'))) ?>">
中文
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url(UOJContext::requestURI(), array('params' => array('locale' => 'en'))) ?>">
English
</a>
</li>
</ul>
</li>
<?php if (Auth::check()) : ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-fill"></i>
<span class="position-relative">
<?= Auth::id() ?>
<?php if ($new_msg_tot) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_msg_tot > 99 ? "99+" : $new_msg_tot ?>
</span>
<?php endif ?>
</span>
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="<?= HTML::url('/user/' . Auth::id()) ?>">
<?= UOJLocale::get('my profile') ?>
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/user_msg') ?>">
<?= UOJLocale::get('private message') ?>
<?php if ($new_user_msg_num) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_user_msg_num > 99 ? "99+" : $new_user_msg_num ?>
</span>
<?php endif ?>
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/user/' . Auth::id() . '/system_msg') ?>">
<?= UOJLocale::get('system message') ?>
<?php if ($new_system_msg_num) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_system_msg_num > 99 ? "99+" : $new_system_msg_num ?>
</span>
<?php endif ?>
</a>
</li>
<?php if (isSuperUser(Auth::user())) : ?>
<li>
<a class="dropdown-item" href="<?= HTML::url('/super_manage') ?>">
<?= UOJLocale::get('system manage') ?>
</a>
</li>
<?php endif ?>
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/logout?_token=' . crsf_token()) ?>">
<?= UOJLocale::get('logout') ?>
</a>
</li>
</ul>
</li>
<?php else : ?>
<li class="nav-item">
<a class="nav-link" href="<?= HTML::url('/login') ?>">
<i class="bi bi-box-arrow-in-right"></i>
<?= UOJLocale::get('login') ?>
</a>
</li>
<?php if (UOJConfig::$data['switch']['open-register'] || !DB::selectCount("SELECT COUNT(*) FROM user_info")) : ?>
<li class="nav-item">
<a class="nav-link" href="<?= HTML::url('/register') ?>">
<i class="bi bi-person-plus-fill"></i>
<?= UOJLocale::get('register') ?>
</a>
</li>
<?php endif ?>
<?php endif ?>
</ul>
<?php uojIncludeView('nav-right', [
'new_msg_tot' => $new_msg_tot,
'new_user_msg_num' => $new_user_msg_num,
'new_system_msg_num' => $new_system_msg_num,
]) ?>
</div>
</div>
</div>

View File

@ -4,7 +4,7 @@ $new_system_msg_num = DB::selectCount("select count(*) from user_system_msg wher
$new_msg_tot = $new_user_msg_num + $new_system_msg_num;
?>
<div class="navbar navbar-light navbar-expand-md bg-white shadow-sm
<div class="navbar navbar-light navbar-expand-md bg-body shadow-sm
<?php if (!isset($disable_navbar_margin_bottom)) : ?>
mb-4
<?php endif ?>
@ -99,98 +99,11 @@ mb-4
<hr class="d-md-none text-muted">
<ul class="nav navbar-nav ms-md-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-translate"></i>
<?= UOJLocale::get('_common_name') ?>
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="<?= HTML::url(UOJContext::requestURI(), array('params' => array('locale' => 'zh-cn'))) ?>">
中文
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url(UOJContext::requestURI(), array('params' => array('locale' => 'en'))) ?>">
English
</a>
</li>
</ul>
</li>
<?php if (Auth::check()) : ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-fill"></i>
<span class="position-relative">
<?= Auth::id() ?>
<?php if ($new_msg_tot) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_msg_tot > 99 ? "99+" : $new_msg_tot ?>
</span>
<?php endif ?>
</span>
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="<?= HTML::url('/user/' . Auth::id()) ?>">
<?= UOJLocale::get('my profile') ?>
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/user_msg') ?>">
<?= UOJLocale::get('private message') ?>
<?php if ($new_user_msg_num) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_user_msg_num > 99 ? "99+" : $new_user_msg_num ?>
</span>
<?php endif ?>
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/user/' . Auth::id() . '/system_msg') ?>">
<?= UOJLocale::get('system message') ?>
<?php if ($new_system_msg_num) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_system_msg_num > 99 ? "99+" : $new_system_msg_num ?>
</span>
<?php endif ?>
</a>
</li>
<?php if (isSuperUser(Auth::user())) : ?>
<li>
<a class="dropdown-item" href="<?= HTML::url('/super_manage') ?>">
<?= UOJLocale::get('system manage') ?>
</a>
</li>
<?php endif ?>
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/logout?_token=' . crsf_token()) ?>">
<?= UOJLocale::get('logout') ?>
</a>
</li>
</ul>
</li>
<?php else : ?>
<li class="nav-item">
<a class="nav-link" href="<?= HTML::url('/login') ?>">
<i class="bi bi-box-arrow-in-right"></i>
<?= UOJLocale::get('login') ?>
</a>
</li>
<?php if (UOJConfig::$data['switch']['open-register'] || !DB::selectCount("SELECT COUNT(*) FROM user_info")) : ?>
<li class="nav-item">
<a class="nav-link" href="<?= HTML::url('/register') ?>">
<i class="bi bi-person-plus-fill"></i>
<?= UOJLocale::get('register') ?>
</a>
</li>
<?php endif ?>
<?php endif ?>
</ul>
<?php uojIncludeView('nav-right', [
'new_msg_tot' => $new_msg_tot,
'new_user_msg_num' => $new_user_msg_num,
'new_system_msg_num' => $new_system_msg_num,
]) ?>
</div>
</div>

118
web/app/views/nav-right.php Normal file
View File

@ -0,0 +1,118 @@
<ul class="nav navbar-nav ms-md-auto">
<li class="nav-item dropdown">
<button class="btn btn-link nav-link py-2 px-0 px-lg-2 dropdown-toggle d-flex align-items-center" id="bd-theme" type="button" aria-expanded="false" data-bs-toggle="dropdown" data-bs-display="static">
<i class="bi bi-circle-half theme-icon-active" data-icon="bi-circle-half"></i>
<span class="d-lg-none">&nbsp;切换主题</span>
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme" style="--bs-dropdown-min-width: 6rem;">
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto">
<i class="bi bi-circle-half me-2 opacity-50 theme-icon" data-icon="bi-circle-half"></i>
系统
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light">
<i class="bi bi-sun-fill me-2 opacity-50 theme-icon" data-icon="bi-sun-fill"></i>
亮色
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark">
<i class="bi bi-moon-stars-fill me-2 opacity-50 theme-icon" data-icon="bi-moon-stars-fill"></i>
暗色
</button>
</li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-translate"></i>
<?= UOJLocale::get('_common_name') ?>
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="<?= HTML::url(UOJContext::requestURI(), array('params' => array('locale' => 'zh-cn'))) ?>">
中文
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url(UOJContext::requestURI(), array('params' => array('locale' => 'en'))) ?>">
English
</a>
</li>
</ul>
</li>
<?php if (Auth::check()) : ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-fill"></i>
<span class="position-relative">
<?= Auth::id() ?>
<?php if ($new_msg_tot) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_msg_tot > 99 ? "99+" : $new_msg_tot ?>
</span>
<?php endif ?>
</span>
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="<?= HTML::url('/user/' . Auth::id()) ?>">
<?= UOJLocale::get('my profile') ?>
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/user_msg') ?>">
<?= UOJLocale::get('private message') ?>
<?php if ($new_user_msg_num) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_user_msg_num > 99 ? "99+" : $new_user_msg_num ?>
</span>
<?php endif ?>
</a>
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/user/' . Auth::id() . '/system_msg') ?>">
<?= UOJLocale::get('system message') ?>
<?php if ($new_system_msg_num) : ?>
<span class="badge bg-danger rounded-pill">
<?= $new_system_msg_num > 99 ? "99+" : $new_system_msg_num ?>
</span>
<?php endif ?>
</a>
</li>
<?php if (isSuperUser(Auth::user())) : ?>
<li>
<a class="dropdown-item" href="<?= HTML::url('/super_manage') ?>">
<?= UOJLocale::get('system manage') ?>
</a>
</li>
<?php endif ?>
<li>
<hr class="dropdown-divider">
</li>
<li>
<a class="dropdown-item" href="<?= HTML::url('/logout?_token=' . crsf_token()) ?>">
<?= UOJLocale::get('logout') ?>
</a>
</li>
</ul>
</li>
<?php else : ?>
<li class="nav-item">
<a class="nav-link" href="<?= HTML::url('/login') ?>">
<i class="bi bi-box-arrow-in-right"></i>
<?= UOJLocale::get('login') ?>
</a>
</li>
<?php if (UOJConfig::$data['switch']['open-register'] || !DB::selectCount("SELECT COUNT(*) FROM user_info")) : ?>
<li class="nav-item">
<a class="nav-link" href="<?= HTML::url('/register') ?>">
<i class="bi bi-person-plus-fill"></i>
<?= UOJLocale::get('register') ?>
</a>
</li>
<?php endif ?>
<?php endif ?>
</ul>

View File

@ -11,7 +11,7 @@ if (!isset($ShowPageFooter)) {
</script>
<?php endif ?>
<footer class="bg-white text-muted pt-3 pb-4 mt-4" style="font-size: 0.9em">
<footer class="bg-body text-muted pt-3 pb-4 mt-4" style="font-size: 0.9em">
<div class="container d-lg-flex justify-content-lg-between">
<div>
<div>

View File

@ -188,7 +188,7 @@ if (!isset($PageContainerClass)) {
<?php endif ?>
<?php if (isset($REQUIRE_LIB['hljs'])) : ?>
<?= HTML::css_link('/css/highlightjs.github.min.css?v=11.6.0-20221005') ?>
<?= HTML::css_link('/css/highlightjs.github.min.css?v=11.6.0-20230223') ?>
<?= HTML::js_src('/js/highlightjs.min.js?v=11.6.0-20221005') ?>
<script>
$(document).ready(function() {
@ -242,7 +242,7 @@ if (!isset($PageContainerClass)) {
<body class="d-flex flex-column min-vh-100
<?php if ($ShowPageHeader) : ?>
bg-light
bg-body-tertiary
<?php endif ?>">
<?php if ($ShowPageHeader) : ?>
<?php uojIncludeView($PageNav, array('REQUIRE_LIB' => $REQUIRE_LIB)) ?>

View File

@ -1 +1,141 @@
pre code.hljs{display:block;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#24292e;background:rgba(var(--bs-light-rgb))}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}
:root,
[data-bs-theme='light'] {
--hljs-color: #24292e;
--hljs-keyword: #d73a49;
--hljs-title: #6f42c1;
--hljs-attr: #005cc5;
--hljs-string: #032f62;
--hljs-symbol: #e36209;
--hljs-comment: #6a737d;
--hljs-quote: #22863a;
--hljs-subst: #24292e;
--hljs-selection: #005cc5;
--hljs-bullet: #735c0f;
--hljs-emphasis: #24292e;
--hljs-strong: #24292e;
--hljs-addition: #22863a;
--hljs-addition-bg: #f0fff4;
--hljs-deletion: #b31d28;
--hljs-deletion-bg: #ffeef0;
}
[data-bs-theme='dark'] {
--hljs-color: #c9d1d9;
--hljs-keyword: #ff7b72;
--hljs-title: #d2a8ff;
--hljs-attr: #79c0ff;
--hljs-string: #a5d6ff;
--hljs-symbol: #ffa657;
--hljs-comment: #8b949e;
--hljs-quote: #7ee787;
--hljs-subst: #c9d1d9;
--hljs-selection: #1f6feb;
--hljs-bullet: #f2cc60;
--hljs-emphasis: #c9d1d9;
--hljs-strong: #c9d1d9;
--hljs-addition: #aff5b4;
--hljs-addition-bg: #033a16;
--hljs-deletion: #ffdcd7;
--hljs-deletion-bg: #67060c;
}
pre code.hljs {
display: block;
overflow-x: auto;
}
code.hljs {
padding: 3px 5px;
}
.hljs {
color: var(--hljs-color);
background: var(--bs-tertiary-bg);
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
color: var(--hljs-keyword);
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
color: var(--hljs-title);
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id,
.hljs-variable {
color: var(--hljs-attr);
}
.hljs-meta .hljs-string,
.hljs-regexp,
.hljs-string {
color: var(--hljs-string);
}
.hljs-built_in,
.hljs-symbol {
color: var(--hljs-symbol);
}
.hljs-code,
.hljs-comment,
.hljs-formula {
color: var(--hljs-comment);
}
.hljs-name,
.hljs-quote,
.hljs-selector-pseudo,
.hljs-selector-tag {
color: var(--hljs-quote);
}
.hljs-subst {
color: var(--hljs-subst);
}
.hljs-section {
color: var(--hljs-selection);
font-weight: 700;
}
.hljs-bullet {
color: var(--hljs-bullet);
}
.hljs-emphasis {
color: var(--hljs-emphasis);
font-style: italic;
}
.hljs-strong {
color: var(--hljs-strong);
font-weight: 700;
}
.hljs-addition {
color: var(--hljs-addition);
background-color: var(--hljs-addition-bg);
}
.hljs-deletion {
color: var(--hljs-deletion);
background-color: var(--hljs-deletion-bg);
}

View File

@ -294,7 +294,7 @@ h6,
.markdown-body pre {
padding: 1em;
background-color: rgba(var(--bs-light-rgb));
background-color: var(--bs-tertiary-bg);
}
.markdown-body table th[align='left'] {
@ -541,3 +541,93 @@ form.form-horizontal {
grid-column: 2;
grid-row: 2;
}
/* Username color */
.uoj-username,
.uoj-username-blue {
color: #1e88e5;
}
.uoj-username-green {
color: #2da44e;
}
.uoj-username-pink {
color: #e85aad;
}
.uoj-username-red {
color: #f32a38;
}
.uoj-username-orange {
color: #f57c00;
}
.uoj-username-cyan {
color: #00acc1;
}
.uoj-username-purple {
color: #9d3dcf;
}
.uoj-username-gray {
color: #707070;
}
.uoj-username-brown {
color: #996600;
}
/* Dark: Username color */
[data-bs-theme='dark'] .uoj-username,
[data-bs-theme='dark'] .uoj-username-blue {
color: #218bff;
}
[data-bs-theme='green'] .uoj-username-green {
color: #19a140;
}
[data-bs-theme='dark'] .uoj-username-pink {
color: #bf3989;
}
[data-bs-theme='dark'] .uoj-username-red {
color: #cf222e;
}
[data-bs-theme='dark'] .uoj-username-orange {
color: #e16f24;
}
[data-bs-theme='dark'] .uoj-username-cyan {
color: #00acc1;
}
[data-bs-theme='dark'] .uoj-username-purple {
color: #9366e5;
}
[data-bs-theme='dark'] .uoj-username-gray {
color: #8c959f;
}
[data-bs-theme='dark'] .uoj-username-brown {
color: #996f1c;
}
/* Dark: Table Colors */
[data-bs-theme='dark'] .table-success {
--bs-table-bg: #d1e7ddb9;
--bs-table-border-color: #bcd0c7b9;
}
[data-bs-theme='dark'] .table-warning {
--bs-table-bg: #fff3cdb9;
--bs-table-border-color: #e6dbb9b9;
}

View File

@ -100,7 +100,6 @@ function blog_editor_init(name, editor_config) {
enabled: false,
},
wordWrap: 'on',
theme: 'vs',
unicodeHighlight: {
ambiguousCharacters: false,
},

File diff suppressed because it is too large Load Diff

View File

@ -123,7 +123,7 @@ function getUserLink(username, realname, color) {
return '';
}
var text = username;
var style = '';
var className = 'uoj-username';
if (username.charAt(0) == '@') {
username = username.substr(1);
}
@ -131,16 +131,16 @@ function getUserLink(username, realname, color) {
text = text + ' <span class="uoj-realname d-inline-block">(' + realname + ')</span>';
}
if (color) {
style += 'color: ' + color + ';';
className += ' uoj-username-' + color;
}
return '<a class="uoj-username" href="' + uojHome + '/user/' + username + '" ' + 'style="' + style + '">' + text + '</a>';
return '<a class="' + className + '" href="' + uojHome + '/user/' + username + '">' + text + '</a>';
}
function getUserSpan(username, realname, color) {
if (!username) {
return '';
}
var text = username;
var style = '';
var className = 'uoj-username';
if (username.charAt(0) == '@') {
username = username.substr(1);
}
@ -148,9 +148,9 @@ function getUserSpan(username, realname, color) {
text = text + ' <span class="uoj-realname d-inline-block">(' + realname + ')</span>';
}
if (color) {
style += 'color: ' + color + ';';
className += ' uoj-username-' + color;
}
return '<span class="uoj-username" ' + 'style="' + style + '">' + text + '</span>';
return '<span class="' + className + '">' + text + '</span>';
}
function replaceWithHighlightUsername() {
@ -602,6 +602,8 @@ function require_monaco(config, callback) {
.script('/js/monaco-editor/min/vs/editor/editor.main.nls.zh-cn.js').wait()
.script('/js/monaco-editor/min/vs/editor/editor.main.js').wait(function() {
$LAB.script('/js/monaco-themes.js').wait(function() {
update_monaco_theme(getPreferredTheme());
if (config.markdown) {
$LAB.script('/js/blog-editor/monaco-markdown.js').wait(callback);
} else {
@ -611,6 +613,16 @@ function require_monaco(config, callback) {
});
}
function update_monaco_theme(theme) {
if (!('monaco' in window)) return;
if (theme == 'dark') {
monaco.editor.setTheme('github-dark');
} else {
monaco.editor.setTheme('github');
}
}
function get_monaco_mode(lang) {
switch (lang) {
case 'C++':
@ -1365,7 +1377,7 @@ $.fn.problem_conf_preview = function(problem_conf) {
res += key + ' ' + value + '\n';
}
$(this).html('<pre class="bg-light mb-0 p-3"><code>' + res + '</code></pre>');
$(this).html('<pre class="bg-body-tertiary mb-0 p-3"><code>' + res + '</code></pre>');
});
}
@ -1698,7 +1710,8 @@ function showCommentReplies(id, replies) {
).append(
user_can_hide_comment
? $('<li class="list-inline-item" />').append(
$('<a href="#" class="text-warning-emphasis text-decoration-none p-0" />').data('comment-id', reply.id).text('隐藏').click(function() {
$('<a href="#" class="text-warning-emphasis text-decoration-none p-0" />').data('comment-id', reply.id).text('隐藏').click(function(event) {
event.preventDefault();
toggleModalHideComment(reply.id, reply.content);
})
)
@ -2118,8 +2131,75 @@ $(document).ready(function() {
});
});
/**
* Fallback user avatar
*/
$(document).ready(function() {
$('img.uoj-user-avatar').on('error', function() {
// same as gravatar d=mm
$(this).attr('src', 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA4NzIgODcyIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAwaDg3MnY4NzJIMHoiLz48cGF0aCBmaWxsPSIjYzVjNWM1IiBkPSJNMCAwdjg3MmgxMTBhMzI3IDM4MyAwIDAxMjM2LTM0NSAxOTUgMTk1IDAgMDEtMTA0LTE3MiAxOTUgMTk1IDAgMDExOTUtMTk1IDE5NSAxOTUgMCAwMTE5NSAxOTUgMTk1IDE5NSAwIDAxLTEwNiAxNzMgMzI3IDM4MyAwIDAxMjM2IDM0NGgxMTBWMHoiLz48L3N2Zz4=');
});
});
function getPreferredTheme() {
if (!('localStorage' in window)) return 'light';
var storedTheme = localStorage.getItem('theme');
if (storedTheme) {
return storedTheme;
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
/**
* Theme toggler
*/
(function() {
if (!('localStorage' in window)) return;
var storedTheme = localStorage.getItem('theme');
function setTheme(theme) {
if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark');
update_monaco_theme('dark');
} else {
document.documentElement.setAttribute('data-bs-theme', theme);
update_monaco_theme(theme == 'auto' ? 'light' : theme);
}
}
setTheme(getPreferredTheme());
function showActiveTheme(theme) {
var currentIcon = $('.theme-icon-active').attr('data-icon');
var newIcon = $('[data-bs-theme-value="' + theme +'"] .theme-icon').attr('data-icon');
$('[data-bs-theme-value]').removeClass('active');
$('[data-bs-theme-value="' + theme +'"]').addClass('active');
$('.theme-icon-active')
.removeClass(currentIcon)
.addClass(newIcon)
.attr('data-icon', newIcon);
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() {
if (storedTheme !== 'light' || storedTheme !== 'dark') {
setTheme(getPreferredTheme());
}
});
$(document).ready(function() {
showActiveTheme(getPreferredTheme());
$('[data-bs-theme-value]').click(function() {
var theme = $(this).data('bs-theme-value');
localStorage.setItem('theme', theme);
setTheme(theme);
showActiveTheme(theme);
});
});
})();