Browse Source

Merge branch 'master' of https://github.com/syzoj/syzoj

pull/6/head
Tian Yunhao 6 years ago
parent
commit
99f4d3a880
  1. 3
      .gitignore
  2. 39
      README.en.md
  3. 183
      README.md
  4. 74
      app.js
  5. BIN
      bin/unzip
  6. 16
      config-example.json
  7. 44
      libs/highlight.js
  8. 131
      libs/markdown.js
  9. 7
      libs/timeago/index.js
  10. 53
      libs/timeago/long.json
  11. 53
      libs/timeago/narrow.json
  12. 53
      libs/timeago/short.json
  13. 21
      models/article-comment.js
  14. 21
      models/article.js
  15. 57
      models/common.js
  16. 21
      models/contest.js
  17. 25
      models/contest_player.js
  18. 29
      models/contest_ranklist.js
  19. 24
      models/custom_test.js
  20. 21
      models/file.js
  21. 28
      models/judge_state.js
  22. 35
      models/problem.js
  23. 21
      models/problem_tag.js
  24. 21
      models/problem_tag_map.js
  25. 21
      models/rating_calculation.js
  26. 21
      models/rating_history.js
  27. 21
      models/user.js
  28. 21
      models/user_privilege.js
  29. 21
      models/waiting_judge.js
  30. 19
      modules/admin.js
  31. 21
      modules/api.js
  32. 27
      modules/api_v2.js
  33. 21
      modules/contest.js
  34. 23
      modules/discussion.js
  35. 40
      modules/index.js
  36. 21
      modules/legacy.js
  37. 26
      modules/problem.js
  38. 21
      modules/problem_tag.js
  39. 1
      modules/socketio.js
  40. 23
      modules/submission.js
  41. 21
      modules/user.js
  42. 33
      package.json
  43. 205
      static/mathjax.css
  44. 52
      static/style.css
  45. 57
      utility.js
  46. 2
      views/admin_config.ejs
  47. 2
      views/admin_header.ejs
  48. 2
      views/admin_info.ejs
  49. 2
      views/admin_links.ejs
  50. 6
      views/admin_other.ejs
  51. 2
      views/admin_privilege.ejs
  52. 4
      views/admin_rating.ejs
  53. 2
      views/admin_raw.ejs
  54. 8
      views/admin_rejudge.ejs
  55. 15
      views/article.ejs
  56. 3
      views/article_edit.ejs
  57. 2
      views/contest_edit.ejs
  58. 9
      views/contest_ranklist.ejs
  59. 30
      views/contests.ejs
  60. 23
      views/discussion.ejs
  61. 6
      views/footer.ejs
  62. 27
      views/header.ejs
  63. 41
      views/index.ejs
  64. 4
      views/login.ejs
  65. 2
      views/page.ejs
  66. 4
      views/problem.ejs
  67. 4
      views/problem_data.ejs
  68. 6
      views/problem_edit.ejs
  69. 6
      views/problem_import.ejs
  70. 8
      views/problem_manage.ejs
  71. 6
      views/problem_tag_edit.ejs
  72. 19
      views/problems.ejs
  73. 4
      views/ranklist.ejs
  74. 4
      views/status_label.ejs
  75. 4
      views/submission.ejs
  76. 68
      views/submissions_item.ejs
  77. 4
      views/user.ejs
  78. 8
      views/user_edit.ejs

3
.gitignore vendored

@ -3,6 +3,7 @@ config.json
yarn.lock
*.db
.git.*
package-lock.json
# Logs
logs
@ -62,4 +63,4 @@ Session.vim
# auto-generated tag files
tags
# End of https://www.gitignore.io/api/vim
# End of https://www.gitignore.io/api/vim

39
README.en.md

@ -0,0 +1,39 @@
# SYZOJ 2
[中文](README.md) | English
An online judge system for algorithm competition.
This project is the **official** successor and rewritten version of the original Python/Flask version of SYZOJ, which is authorized by the original author [@Chenyao2333](https://github.com/Chenyao2333).
Currently maintained by [LibreOJ](https://loj.ac).
# Deploying
Currently, the tutorial for deploying is only available in Chinese. It's [部署指南](https://github.com/syzoj/syzoj/wiki/%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97) in this project's wiki.
Join QQ group [565280992](https://jq.qq.com/?_wv=1027&k=5JQZWwd) or Telegram group [@lojdev](https://t.me/lojdev) for help.
# Upgrading
Because of updates to the database structure, users who upgrade from a commit BEFORE [d5bcbe8fb79e80f9d603b764ac787295cceffa34](https://github.com/syzoj/syzoj/commit/d5bcbe8fb79e80f9d603b764ac787295cceffa34) (Feb 21, 2018) **MUST** perform the following SQL on the database.
```sql
ALTER TABLE `judge_state` ADD `is_public` TINYINT(1) NOT NULL AFTER `compilation`;
UPDATE `judge_state` JOIN `problem` ON `problem`.`id` = `judge_state`.`problem_id` SET `judge_state`.`is_public` = `problem`.`is_public`;
ALTER TABLE `syzoj`.`judge_state` ADD INDEX `judge_state_is_public` (`id`, `is_public`, `type_info`, `type`);
```
Who upgrade from a commit BEFORE [26d66ceef24fbb35481317453bcb89ead6c69076](https://github.com/syzoj/syzoj/commit/26d66ceef24fbb35481317453bcb89ead6c69076) (Nov 5, 2018) **MUST** perform the following SQL on the database.
```sql
ALTER TABLE `contest_player` CHANGE `score_details` `score_details` JSON NOT NULL;
ALTER TABLE `contest_ranklist` CHANGE `ranking_params` `ranking_params` JSON NOT NULL;
ALTER TABLE `contest_ranklist` CHANGE `ranklist` `ranklist` JSON NOT NULL;
ALTER TABLE `custom_test` CHANGE `result` `result` JSON NOT NULL;
ALTER TABLE `judge_state` CHANGE `compilation` `compilation` JSON NOT NULL;
ALTER TABLE `judge_state` CHANGE `result` `result` JSON NOT NULL;
```
Who upgraded from a commit BEFORE [84b9e2d7b51e4ed3ab426621b66cf5ae9e1e1c23](https://github.com/syzoj/syzoj/commit/84b9e2d7b51e4ed3ab426621b66cf5ae9e1e1c23) (Nov 6, 2018) **MUST** perform the following SQL on the database.
```sql
ALTER TABLE `problem` ADD `publicize_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `type`;
```

183
README.md

@ -1,174 +1,39 @@
# SYZOJ 2
An OnlineJudge System for OI.
中文 | [English](README.en.md)
The UI is powered by [Semantic UI](http://semantic-ui.com/).
Template designed & coded by [Sengxian](https://www.sengxian.com) and [Menci](https://men.ci).
一个用于算法竞赛的在线评测系统。
# Upgrading
Because of an update to the database structure, users who upgrade from a commit BEFORE 4c673956959532d61b8f9ba0be3191a054b4371a **MUST** perform the following SQL on the database:
```sql
ALTER TABLE `judge_state` ADD `is_public` TINYINT(1) NOT NULL AFTER `compilation`;
UPDATE `judge_state` JOIN `problem` ON `problem`.`id` = `judge_state`.`problem_id` SET `judge_state`.`is_public` = `problem`.`is_public`;
ALTER TABLE `syzoj`.`judge_state` ADD INDEX `judge_state_is_public` (`id`, `is_public`, `type_info`, `type`);
```
# Deploying
**Warning** The following content is **outdated**. Please refer to https://syzoj-demo.t123yh.xyz:20170/article/1 for a detailed guide and a demo server.
There's currently *no* stable version of SYZOJ 2, but you can use the unstable version from git.
```
git clone https://github.com/syzoj/syzoj
cd syzoj
```
此项目为重写过的、原 Python/Flask 版 SYZOJ 的**官方**后继版本,由原作者 [@Chenyao2333](https://github.com/Chenyao2333) 授权。
Install dependencies with `npm install` or `yarn`. Also, follow the instructions [here](https://www.npmjs.com/package/node-7z#installation) to install `7z` executable used by the `node-7z` package.
目前由 [LibreOJ](https://loj.ac) 维护。
Copy `config-example.json` to `config.json`, and make necessary changes.
# 部署
见本项目 Wiki 中的 [部署指南](https://github.com/syzoj/syzoj/wiki/%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97)。
## Database
SYZOJ 2 uses [Sequelize](http://sequelizejs.com), which supports many database systems, including MySQL and Sqlite.
加入 QQ 群 [565280992](https://jq.qq.com/?_wv=1027&k=5JQZWwd) 或 Telegram 群 [@lojdev](https://t.me/lojdev) 以取得帮助。
By default it use the Sqlite database `syzoj.db`, you can change it in `config.json`
# 升级须知
因为一些数据库结构的更新,从该 commit [d5bcbe8fb79e80f9d603b764ac787295cceffa34](https://github.com/syzoj/syzoj/commit/d5bcbe8fb79e80f9d603b764ac787295cceffa34)(2018 年 4 月 21 日)前更新的用户**必须**在其数据库上执行以下 SQL 语句。
## Security
You should change the `session_secret` and `judge_token` in `config.json`.
# Administration
In the database, the `is_admin` field in `user` table describes whether a user is admin or not.
To make a user be an admin, the only way is via database.
# Judge
SYZOJ 2 uses a Docker-based sandboxed judger. Please go to [syzoj-judge](https://github.com/syzoj/syzoj-judge).
# Advanced
## System Service
### Run SYZOJ 2
Add SYZOJ 2 to system services.
``` bash
vim [syzoj2 path]/runsyzoj
```
Edit `runsyzoj` as as follows.
``` bash
#!/bin/bash
cd [syzoj2 path]
npm start > log.txt 2>&1
```
**Please change `[syzoj2 path]`.**
### Run SYZOJ-JUDGE
Add SYZOJ-JUDGE to system services.
``` bash
vim [syzoj-judge path]/runjudge
```
Edit `runjudge` as as follows.
``` bash
#!/bin/bash
cd [syzoj-judge path]
npm start > jlog.txt 2>&1
```
**Please change `[syzoj-judge path]`.**
### Add To System Service
``` bash
vim /etc/systemd/system/syzoj.service
```
Edit `/etc/systemd/system/syzoj.service` as as follows.
``` bash
[Unit]
Description=SYZOJ Online Judge
After=network.target
[Service]
Type=simple
PIDFile=/run/syzoj.pid
WorkingDirectory=[syzoj2 path]
ExecStart=[syzoj2 path]/runsyzoj
StandardOutput=null
StandardError=null
[Install]
WantedBy=multi-user.target
```
**Please change `[syzoj2 path]`.**
``` bash
vim /etc/systemd/system/syzoj-judge.service
```
Edit `/etc/systemd/system/syzoj-judge.service` as as follows.
``` bash
[Unit]
Description=SYZOJ Judge Daemon
After=network.target
[Service]
Type=simple
PIDFile=/run/syzoj-judge.pid
WorkingDirectory=[syzoj-judge path]
ExecStart=[syzoj-judge path]/runjudge
StandardOutput=null
StandardError=null
[Install]
WantedBy=multi-user.target
```
**Please change `[syzoj-judge path]`.**
### Usage
#### Start
``` bash
systemctl start syzoj
systemctl start syzoj-judge
```
#### Stop
``` bash
systemctl stop syzoj
systemctl stop syzoj-judge
```
#### Restart
``` bash
systemctl restart syzoj
systemctl restart syzoj-judge
```sql
ALTER TABLE `judge_state` ADD `is_public` TINYINT(1) NOT NULL AFTER `compilation`;
UPDATE `judge_state` JOIN `problem` ON `problem`.`id` = `judge_state`.`problem_id` SET `judge_state`.`is_public` = `problem`.`is_public`;
ALTER TABLE `syzoj`.`judge_state` ADD INDEX `judge_state_is_public` (`id`, `is_public`, `type_info`, `type`);
```
## 邮件配置
### register_mail
是否启用注册邮件验证。
从该 commit [26d66ceef24fbb35481317453bcb89ead6c69076](https://github.com/syzoj/syzoj/commit/26d66ceef24fbb35481317453bcb89ead6c69076)(2018 年 11 月 5 日)前更新的用户**必须**在其数据库上执行以下 SQL 语句。
### email\_jwt\_secret
用于 Email Token 签名的 secret,脸滚键盘随意填写即可。
### email
#### Sendmail 直接发送(成功率低,不推荐)
```js
"email": {
"method": "sendmail",
"options": {
"address": "xxxx", // 发件人地址
}
},
```sql
ALTER TABLE `contest_player` CHANGE `score_details` `score_details` JSON NOT NULL;
ALTER TABLE `contest_ranklist` CHANGE `ranking_params` `ranking_params` JSON NOT NULL;
ALTER TABLE `contest_ranklist` CHANGE `ranklist` `ranklist` JSON NOT NULL;
ALTER TABLE `custom_test` CHANGE `result` `result` JSON NOT NULL;
ALTER TABLE `judge_state` CHANGE `compilation` `compilation` JSON NOT NULL;
ALTER TABLE `judge_state` CHANGE `result` `result` JSON NOT NULL;
```
#### 阿里云邮件推送服务(成功率较高)
```js
"email": {
"method": "aliyundm",
"options": {
"AccessKeyId": "xxxx",
"AccessKeySecret": "xxxx",
"AccountName": "xxxx" // 发件邮箱
}
},
```
从该 commit [84b9e2d7b51e4ed3ab426621b66cf5ae9e1e1c23](https://github.com/syzoj/syzoj/commit/84b9e2d7b51e4ed3ab426621b66cf5ae9e1e1c23)(2018 年 11 月 6 日)前更新的用户**必须**在其数据库上执行以下 SQL 语句。
#### SMTP 服务
```js
"email": {
"method": "smtp",
"options": {
"host": "smtp.163.com",
"port": 465,
"username": "xxx@163.com",
"password": "xxx",
"allowUnauthorizedTls": false
}
},
```sql
ALTER TABLE `problem` ADD `publicize_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `type`;
```

74
app.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let fs = require('fs'),
path = require('path');
@ -31,7 +10,7 @@ const options = commandLineArgs(optionDefinitions);
global.syzoj = {
rootDir: __dirname,
config: require(options.config),
config: require('object-assign-deep')({}, require('./config-example.json'), require(options.config)),
configDir: options.config,
models: [],
modules: [],
@ -53,7 +32,7 @@ global.syzoj = {
});
// Set assets dir
app.use(Express.static(__dirname + '/static'));
app.use(Express.static(__dirname + '/static', { maxAge: syzoj.production ? '1y' : 0 }));
// Set template engine ejs
app.set('view engine', 'ejs');
@ -88,11 +67,50 @@ global.syzoj = {
},
async connectDatabase() {
let Sequelize = require('sequelize');
let Op = Sequelize.Op;
let operatorsAliases = {
$eq: Op.eq,
$ne: Op.ne,
$gte: Op.gte,
$gt: Op.gt,
$lte: Op.lte,
$lt: Op.lt,
$not: Op.not,
$in: Op.in,
$notIn: Op.notIn,
$is: Op.is,
$like: Op.like,
$notLike: Op.notLike,
$iLike: Op.iLike,
$notILike: Op.notILike,
$regexp: Op.regexp,
$notRegexp: Op.notRegexp,
$iRegexp: Op.iRegexp,
$notIRegexp: Op.notIRegexp,
$between: Op.between,
$notBetween: Op.notBetween,
$overlap: Op.overlap,
$contains: Op.contains,
$contained: Op.contained,
$adjacent: Op.adjacent,
$strictLeft: Op.strictLeft,
$strictRight: Op.strictRight,
$noExtendRight: Op.noExtendRight,
$noExtendLeft: Op.noExtendLeft,
$and: Op.and,
$or: Op.or,
$any: Op.any,
$all: Op.all,
$values: Op.values,
$col: Op.col
};
this.db = new Sequelize(this.config.db.database, this.config.db.username, this.config.db.password, {
host: this.config.db.host,
dialect: this.config.db.dialect,
storage: this.config.db.storage ? this.utils.resolvePath(this.config.db.storage) : null,
logging: syzoj.production ? false : syzoj.log
dialect: 'mysql',
logging: syzoj.production ? false : syzoj.log,
timezone: require('moment')().format('Z'),
operatorsAliases: operatorsAliases
});
global.Promise = Sequelize.Promise;
this.db.countQuery = async (sql, options) => (await this.db.query(`SELECT COUNT(*) FROM (${sql}) AS \`__tmp_table\``, options))[0][0]['COUNT(*)'];
@ -132,7 +150,7 @@ global.syzoj = {
let FileStore = require('session-file-store')(Session);
let sessionConfig = {
secret: this.config.session_secret,
cookie: {},
cookie: { httpOnly: false },
rolling: true,
saveUninitialized: true,
resave: true,
@ -140,7 +158,7 @@ global.syzoj = {
};
if (syzoj.production) {
app.set('trust proxy', 1);
sessionConfig.cookie.secure = true;
sessionConfig.cookie.secure = false;
}
app.use(Session(sessionConfig));

BIN
bin/unzip

Binary file not shown.

16
config-example.json

@ -3,12 +3,15 @@
"hostname": "127.0.0.1",
"port": "5283",
"db": {
"database": null,
"username": null,
"database": "syzoj",
"username": "syzoj",
"password": null,
"host": null,
"dialect": "sqlite",
"storage": "syzoj.db"
"host": "127.0.0.1"
},
"logo": {
"url": null,
"width": null,
"height": null
},
"register_mail": false,
"email": {
@ -54,7 +57,8 @@
"problem": 50,
"problem_statistics": 10,
"judge_state": 10,
"ranklist": 20,
"ranklist_index": 20,
"ranklist": 50,
"discussion": 10,
"article_comment": 10,
"contest": 10,

44
libs/highlight.js

@ -0,0 +1,44 @@
/*
* This file is part of moemark-renderer.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* moemark-renderer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* moemark-renderer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with moemark-renderer. If not, see <http://www.gnu.org/licenses/>.
*/
const pygmentize = require('pygmentize-bundled-cached');
function escapeHTML(s) {
// Code from http://stackoverflow.com/questions/5251520/how-do-i-escape-some-html-in-javascript/5251551
return s.replace(/[^0-9A-Za-z ]/g, (c) => {
return "&#" + c.charCodeAt(0) + ";";
});
}
module.exports = (code, lang, cb) => {
pygmentize({
lang: lang,
format: 'html',
options: {
nowrap: true,
classprefix: 'pl-'
}
}, code, (err, res) => {
if (err || res.toString() === 'undefined') {
cb(escapeHTML(code));
} else {
cb(res);
}
});
}

131
libs/markdown.js

@ -0,0 +1,131 @@
/*
* This file is part of moemark-renderer.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* moemark-renderer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* moemark-renderer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with moemark-renderer. If not, see <http://www.gnu.org/licenses/>.
*/
const MoeMark = require('moemark');
const katex = require('katex');
const mj = require('mathjax-node');
let defaultCache = {
data: {},
get(key) {
return this.data[key];
},
set(key, val) {
this.data[key] = val;
}
};
let config = {
highlight: require('./highlight')
};
function render(s, cb) {
if (!s.trim()) return cb('');
let mathCnt = 0, mathPending = 0, maths = new Array(), hlCnt = 0, hlPending = 0, hls = new Array(), res, callback, ss, cache = render.cache, cacheOption = render.cacheOption, finished = false;
if (cacheOption.result) {
let x = cache.get('RES_' + s);
if (x !== undefined) return cb(x);
}
MoeMark.setOptions({
lineNumber: false,
math: true,
highlight: function(code, lang) {
if (cacheOption.highlight) {
let x = cache.get('H_' + lang + '_' + code);
if (x !== undefined) return x;
}
let id = hlCnt;
hlCnt++, hlPending++;
config.highlight(code, lang, res => {
hls[id] = res;
if (cacheOption.highlight) cache.set('H_' + lang + '_' + code, res);
if (!--hlPending) finish();
});
return '<span id="hl-' + id + '"></span>';
},
mathRenderer: function(str, display) {
if (cacheOption.math) {
let x = cache.get('M_' + display + '_' + str);
if (x !== undefined) return x;
}
try {
let res = katex.renderToString(str, { displayMode: display });
if (cacheOption.math) cache.set('M_' + display + '_' + str, res);
return res;
} catch (e) {
const id = mathCnt;
mathCnt++, mathPending++;
mj.typeset({
math: str,
format: display ? 'TeX' : 'inline-TeX',
html: true, css: true,
width: 0
}, function (data) {
if (data.errors) maths[id] = '<p><div style="display: inline-block; border: 1px solid #000; "><strong>' + data.errors.toString() + '</strong></div></p>';
else if (display) maths[id] = '<p style="text-align: center; ">' + data.html + '</p>';
else maths[id] = data.html;
if (cacheOption.math) cache.set('M_' + display + '_' + str, maths[id]);
if (!--mathPending) finish();
});
return '<span id="math-' + id + '"></span>';
}
}
});
function finish() {
if (finished || !res || mathPending || hlPending) return;
finished = true;
if (maths.length || hls.length) {
let x = new (require('jsdom').JSDOM)().window.document.createElement('div');
x.innerHTML = res;
for (let i = 0; i < maths.length; i++) {
x.querySelector('#math-' + i).outerHTML = maths[i];
}
for (let i = 0; i < hls.length; i++) {
x.querySelector('#hl-' + i).outerHTML = hls[i];
}
res = x.innerHTML;
}
if (cacheOption.result) cache.set('RES_' + s, res);
cb(res);
}
try {
res = MoeMark(s);
if (mathPending == 0 && hlPending == 0) {
finish();
}
} catch(e) {
cb(e);
}
};
render.moemark = MoeMark;
render.cache = defaultCache;
render.cacheOption = {
highlight: true,
math: true,
result: false
};
render.config = config;
module.exports = render;

7
libs/timeago/index.js

@ -0,0 +1,7 @@
module.exports =
{
locale: 'zh',
long: require('./long.json'),
short: require('./short.json'),
narrow: require('./narrow.json')
}

53
libs/timeago/long.json

@ -0,0 +1,53 @@
{
"year": {
"previous": "去年",
"current": "今年",
"next": "明年",
"past": "{0} 年前",
"future": "{0} 年后"
},
"quarter": {
"previous": "上季度",
"current": "本季度",
"next": "下季度",
"past": "{0} 个季度前",
"future": "{0} 个季度后"
},
"month": {
"previous": "上个月",
"current": "本月",
"next": "下个月",
"past": "{0} 个月前",
"future": "{0} 个月后"
},
"week": {
"previous": "上周",
"current": "本周",
"next": "下周",
"past": "{0} 周前",
"future": "{0} 周后"
},
"day": {
"previous": "昨天",
"current": "今天",
"next": "明天",
"past": "{0} 天前",
"future": "{0} 天后"
},
"hour": {
"current": "这一时间 / 此时",
"past": "{0} 小时前",
"future": "{0} 小时后"
},
"minute": {
"current": "此刻",
"past": "{0} 分钟前",
"future": "{0} 分钟后"
},
"second": {
"current": "现在",
"past": "{0} 秒钟前",
"future": "{0} 秒钟后"
},
"now": "现在"
}

53
libs/timeago/narrow.json

@ -0,0 +1,53 @@
{
"year": {
"previous": "去年",
"current": "今年",
"next": "明年",
"past": "{0} 年前",
"future": "{0} 年后"
},
"quarter": {
"previous": "上季度",
"current": "本季度",
"next": "下季度",
"past": "{0} 个季度前",
"future": "{0} 个季度后"
},
"month": {
"previous": "上个月",
"current": "本月",
"next": "下个月",
"past": "{0} 个月前",
"future": "{0} 个月后"
},
"week": {
"previous": "上周",
"current": "本周",
"next": "下周",
"past": "{0} 周前",
"future": "{0} 周后"
},
"day": {
"previous": "昨天",
"current": "今天",
"next": "明天",
"past": "{0} 天前",
"future": "{0} 天后"
},
"hour": {
"current": "这一时间 / 此时",
"past": "{0} 小时前",
"future": "{0} 小时后"
},
"minute": {
"current": "此刻",
"past": "{0} 分钟前",
"future": "{0} 分钟后"
},
"second": {
"current": "现在",
"past": "{0} 秒前",
"future": "{0} 秒后"
},
"now": "现在"
}

53
libs/timeago/short.json

@ -0,0 +1,53 @@
{
"year": {
"previous": "去年",
"current": "今年",
"next": "明年",
"past": "{0} 年前",
"future": "{0} 年后"
},
"quarter": {
"previous": "上季度",
"current": "本季度",
"next": "下季度",
"past": "{0} 个季度前",
"future": "{0} 个季度后"
},
"month": {
"previous": "上个月",
"current": "本月",
"next": "下个月",
"past": "{0} 个月前",
"future": "{0} 个月后"
},
"week": {
"previous": "上周",
"current": "本周",
"next": "下周",
"past": "{0} 周前",
"future": "{0} 周后"
},
"day": {
"previous": "昨天",
"current": "今天",
"next": "明天",
"past": "{0} 天前",
"future": "{0} 天后"
},
"hour": {
"current": "这一时间 / 此时",
"past": "{0} 小时前",
"future": "{0} 小时后"
},
"minute": {
"current": "此刻",
"past": "{0} 分钟前",
"future": "{0} 分钟后"
},
"second": {
"current": "现在",
"past": "{0} 秒前",
"future": "{0} 秒后"
},
"now": "现在"
}

21
models/article-comment.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

21
models/article.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

57
models/common.js

@ -1,23 +1,4 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
class Model {
constructor(record) {
@ -29,9 +10,9 @@ class Model {
let model = this.getModel();
let obj = JSON.parse(JSON.stringify(this.record.get({ plain: true })));
for (let key in obj) {
if (model.tableAttributes[key].json) {
if (model.tableAttributes[key].type instanceof Sequelize.JSON && typeof obj[key] === 'string') {
try {
this[key] = eval(`(${obj[key]})`);
this[key] = JSON.parse(obj[key]);
} catch (e) {
this[key] = {};
}
@ -43,8 +24,7 @@ class Model {
let model = this.getModel();
let obj = JSON.parse(JSON.stringify(this.record.get({ plain: true })));
for (let key in obj) {
if (model.tableAttributes[key].json) obj[key] = JSON.stringify(this[key]);
else obj[key] = this[key];
obj[key] = this[key];
}
return obj;
}
@ -76,7 +56,7 @@ class Model {
}
static async fromID(id) {
return this.fromRecord(this.model.findById(id))
return this.fromRecord(this.model.findByPk(id));
}
static async findOne(options) {
@ -98,7 +78,7 @@ class Model {
return this.model.count({ where: where });
}
static async query(paginate, where, order) {
static async query(paginate, where, order, largeData) {
let records = [];
if (typeof paginate === 'string') {
@ -120,11 +100,34 @@ class Model {
options.limit = parseInt(paginate.perPage);
}
records = await this.model.findAll(options);
if (!largeData) records = await this.model.findAll(options);
else {
let sql = await getSqlFromFindAll(this.model, options);
let i = sql.indexOf('FROM');
sql = 'SELECT id ' + sql.substr(i, sql.length - i);
sql = `SELECT a.* FROM (${sql}) AS b JOIN ${this.model.name} AS a ON a.id = b.id ORDER BY id DESC`;
records = await syzoj.db.query(sql, { model: this.model });
}
}
return records.mapAsync(record => (this.fromRecord(record)));
}
}
function getSqlFromFindAll(Model, options) {
let id = require('uuid')();
return new Promise((resolve, reject) => {
Model.addHook('beforeFindAfterOptions', id, options => {
Model.removeHook('beforeFindAfterOptions', id);
resolve(Model.sequelize.dialect.QueryGenerator.selectQuery(Model.getTableName(), options, Model).slice(0, -1));
return new Promise(() => {});
});
return Model.findAll(options).catch(reject);
});
}
module.exports = Model;

21
models/contest.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

25
models/contest_player.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;
@ -31,7 +10,7 @@ let model = db.define('contest_player', {
user_id: { type: Sequelize.INTEGER },
score: { type: Sequelize.INTEGER },
score_details: { type: Sequelize.TEXT, json: true },
score_details: { type: Sequelize.JSON },
time_spent: { type: Sequelize.INTEGER }
}, {
timestamps: false,
@ -53,7 +32,7 @@ class ContestPlayer extends Model {
contest_id: 0,
user_id: 0,
score: 0,
score_details: '{}',
score_details: {},
time_spent: 0
}, val)));
}

29
models/contest_ranklist.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;
@ -28,8 +7,8 @@ let ContestPlayer = syzoj.model('contest_player');
let model = db.define('contest_ranklist', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
ranking_params: { type: Sequelize.TEXT, json: true },
ranklist: { type: Sequelize.TEXT, json: true }
ranking_params: { type: Sequelize.JSON },
ranklist: { type: Sequelize.JSON }
}, {
timestamps: false,
tableName: 'contest_ranklist'
@ -39,8 +18,8 @@ let Model = require('./common');
class ContestRanklist extends Model {
static async create(val) {
return ContestRanklist.fromRecord(ContestRanklist.model.build(Object.assign({
ranking_params: '{}',
ranklist: '{}'
ranking_params: {},
ranklist: {}
}, val)));
}

24
models/custom_test.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;
@ -38,7 +17,7 @@ let model = db.define('custom_test', {
pending: { type: Sequelize.BOOLEAN },
memory: { type: Sequelize.INTEGER },
result: { type: Sequelize.TEXT('medium'), json: true },
result: { type: Sequelize.JSON },
user_id: { type: Sequelize.INTEGER },
@ -76,6 +55,7 @@ class CustomTest extends Model {
time: 0,
memory: 0,
result: {},
status: 'Waiting',
}, val)));
}

21
models/file.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

28
models/judge_state.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
const randomstring = require('randomstring');
let db = syzoj.db;
@ -45,9 +24,9 @@ let model = db.define('judge_state', {
max_memory: { type: Sequelize.INTEGER },
// For NOI contest
compilation: { type: Sequelize.TEXT('medium'), json: true },
compilation: { type: Sequelize.JSON },
result: { type: Sequelize.TEXT('medium'), json: true },
result: { type: Sequelize.JSON },
user_id: { type: Sequelize.INTEGER },
@ -107,7 +86,8 @@ class JudgeState extends Model {
total_time: null,
max_memory: null,
status: 'Unknown',
result: null,
compilation: {},
result: {},
task_id: randomstring.generate(10),
is_public: false
}, val)));

35
models/problem.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let statisticsStatements = {
fastest:
'\
@ -244,6 +223,8 @@ let model = db.define('problem', {
file_io_input_name: { type: Sequelize.TEXT },
file_io_output_name: { type: Sequelize.TEXT },
publicize_time: { type: Sequelize.DATE },
type: {
type: Sequelize.ENUM,
values: ['traditional', 'submit-answer', 'interaction']
@ -257,7 +238,10 @@ let model = db.define('problem', {
},
{
fields: ['user_id'],
}
},
{
fields: ['publicize_time'],
},
]
});
@ -340,7 +324,8 @@ class Problem extends Model {
await fs.remove(dir);
await fs.ensureDir(dir);
await p7zip.extract(path, dir);
let execFileAsync = Promise.promisify(require('child_process').execFile);
await execFileAsync(__dirname + '/../bin/unzip', ['-j', '-o', '-d', dir, path]);
await fs.move(path, this.getTestdataArchivePath(), { overwrite: true });
});
}
@ -363,6 +348,10 @@ class Problem extends Model {
if (!noLimit && oldCount + !replace > syzoj.config.limit.testdata_filecount) throw new ErrorMessage('数据包中的文件太多。');
await fs.move(filepath, path.join(dir, filename), { overwrite: true });
let execFileAsync = Promise.promisify(require('child_process').execFile);
try { await execFileAsync('dos2unix', [path.join(dir, filename)]); } catch (e) {}
await fs.remove(this.getTestdataArchivePath());
});
}

21
models/problem_tag.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

21
models/problem_tag_map.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

21
models/rating_calculation.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2017 t123yh <t123yh@outlook.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;
const User = syzoj.model('user');

21
models/rating_history.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2017 t123yh <t123yh@outlook.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
const User = syzoj.model('user');
let db = syzoj.db;

21
models/user.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

21
models/user_privilege.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

21
models/waiting_judge.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;

19
modules/admin.js

@ -1,22 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
let Problem = syzoj.model('problem');
let JudgeState = syzoj.model('judge_state');
let Article = syzoj.model('article');

21
modules/api.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let User = syzoj.model('user');
let Problem = syzoj.model('problem');
let File = syzoj.model('file');

27
modules/api_v2.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
app.get('/api/v2/search/users/:keyword*?', async (req, res) => {
try {
let User = syzoj.model('user');
@ -30,7 +9,7 @@ app.get('/api/v2/search/users/:keyword*?', async (req, res) => {
conditions.push({ id: uid });
}
if (keyword != null && String(keyword).length >= 2) {
conditions.push({ username: { like: `%${req.params.keyword}%` } });
conditions.push({ username: { $like: `%${req.params.keyword}%` } });
}
if (conditions.length === 0) {
res.send({ success: true, results: [] });
@ -56,7 +35,7 @@ app.get('/api/v2/search/problems/:keyword*?', async (req, res) => {
let keyword = req.params.keyword || '';
let problems = await Problem.query(null, {
title: { like: `%${req.params.keyword}%` }
title: { $like: `%${req.params.keyword}%` }
}, [['id', 'asc']]);
let result = [];
@ -89,7 +68,7 @@ app.get('/api/v2/search/tags/:keyword*?', async (req, res) => {
let keyword = req.params.keyword || '';
let tags = await ProblemTag.query(null, {
name: { like: `%${req.params.keyword}%` }
name: { $like: `%${req.params.keyword}%` }
}, [['name', 'asc']]);
let result = tags.slice(0, syzoj.config.page.edit_problem_tag_list);

21
modules/contest.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Contest = syzoj.model('contest');
let ContestRanklist = syzoj.model('contest_ranklist');
let ContestPlayer = syzoj.model('contest_player');

23
modules/discussion.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Problem = syzoj.model('problem');
let Article = syzoj.model('article');
let ArticleComment = syzoj.model('article-comment');
@ -190,7 +169,7 @@ app.post('/article/:id/edit', async (req, res) => {
article.title = req.body.title;
article.content = req.body.content;
article.update_time = time;
article.is_notice = res.locals.user && res.locals.user.is_admin && req.body.is_notice === 'on';
article.is_notice = (res.locals.user && res.locals.user.is_admin ? req.body.is_notice === 'on' : article.is_notice);
await article.save();

40
modules/index.js

@ -1,32 +1,16 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let User = syzoj.model('user');
let Article = syzoj.model('article');
let Contest = syzoj.model('contest');
let Problem = syzoj.model('problem');
let Divine = require('syzoj-divine');
let TimeAgo = require('javascript-time-ago');
let zh = require('../libs/timeago');
TimeAgo.locale(zh);
const timeAgo = new TimeAgo('zh-CN');
app.get('/', async (req, res) => {
try {
let ranklist = await User.query([1, 10], { is_show: true }, [[syzoj.config.sorting.ranklist.field, syzoj.config.sorting.ranklist.order]]);
let ranklist = await User.query([1, syzoj.config.page.ranklist_index], { is_show: true }, [[syzoj.config.sorting.ranklist.field, syzoj.config.sorting.ranklist.order]]);
await ranklist.forEachAsync(async x => x.renderInformation());
let notices = (await Article.query(null, { is_notice: true }, [['public_time', 'desc']])).map(article => ({
@ -40,16 +24,20 @@ app.get('/', async (req, res) => {
fortune = Divine(res.locals.user.username, res.locals.user.sex);
}
let where;
if (res.locals.user && await res.locals.user.is_admin) where = {}
else where = { is_public: true };
let contests = await Contest.query([1, 5], where, [['start_time', 'desc']]);
let contests = await Contest.query([1, 5], { is_public: true }, [['start_time', 'desc']]);
let problems = (await Problem.query([1, 5], { is_public: true }, [['publicize_time', 'desc']])).map(problem => ({
id: problem.id,
title: problem.title,
time: timeAgo.format(new Date(problem.publicize_time)),
}));
res.render('index', {
ranklist: ranklist,
notices: notices,
fortune: fortune,
contests: contests,
problems: problems,
links: syzoj.config.links
});
} catch (e) {

21
modules/legacy.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
app.get('/problem', async (req, res) => {
res.redirect('/problems');
});

26
modules/problem.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Problem = syzoj.model('problem');
let JudgeState = syzoj.model('judge_state');
let CustomTest = syzoj.model('custom_test');
@ -35,7 +14,7 @@ app.get('/problems', async (req, res) => {
try {
const sort = req.query.sort || syzoj.config.sorting.problem.field;
const order = req.query.order || syzoj.config.sorting.problem.order;
if (!['id', 'title', 'rating', 'ac_num', 'submit_num', 'ac_rate'].includes(sort) || !['asc', 'desc'].includes(order)) {
if (!['id', 'title', 'rating', 'ac_num', 'submit_num', 'ac_rate', 'publicize_time'].includes(sort) || !['asc', 'desc'].includes(order)) {
throw new ErrorMessage('错误的排序参数。');
}
@ -94,7 +73,7 @@ app.get('/problems/search', async (req, res) => {
let where = {
$or: {
title: { like: `%${req.query.keyword}%` },
title: { $like: `%${req.query.keyword}%` },
id: id
}
};
@ -581,6 +560,7 @@ async function setPublic(req, res, is_public) {
problem.is_public = is_public;
problem.publicizer_id = res.locals.user.id;
problem.publicize_time = new Date();
await problem.save();
JudgeState.model.update(

21
modules/problem_tag.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let ProblemTag = syzoj.model('problem_tag');
app.get('/problems/tag/:id/edit', async (req, res) => {

1
modules/socketio.js

@ -1,4 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const socketio = require("socket.io");
const diff = require("jsondiffpatch");

23
modules/submission.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let JudgeState = syzoj.model('judge_state');
let User = syzoj.model('user');
let Contest = syzoj.model('contest');
@ -113,7 +92,7 @@ app.get('/submissions', async (req, res) => {
}
let paginate = syzoj.utils.paginate(await JudgeState.count(where), req.query.page, syzoj.config.page.judge_state);
let judge_state = await JudgeState.query(paginate, where, [['id', 'desc']]);
let judge_state = await JudgeState.query(paginate, where, [['id', 'desc']], true);
await judge_state.forEachAsync(async obj => obj.loadRelationships());

21
modules/user.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let User = syzoj.model('user');
const RatingCalculation = syzoj.model('rating_calculation');
const RatingHistory = syzoj.model('rating_history');

33
package.json

@ -17,51 +17,54 @@
"express"
],
"author": "Menci",
"license": "GPL-3.0",
"license": "AGPL-3.0",
"bugs": {
"url": "https://github.com/syzoj/syzoj/issues"
},
"homepage": "https://github.com/syzoj/syzoj#readme",
"dependencies": {
"amqplib": "^0.5.2",
"ansi-to-html": "^0.4.2",
"async-lock": "^0.3.9",
"ansi-to-html": "^0.6.8",
"async-lock": "^1.1.3",
"body-parser": "^1.15.2",
"cheerio": "^1.0.0-rc.1",
"command-line-args": "^4.0.7",
"command-line-args": "^5.0.2",
"cookie-parser": "^1.4.3",
"cssfilter": "0.0.10",
"download": "^5.0.3",
"download": "^7.1.0",
"ejs": "^2.5.2",
"express": "^4.14.0",
"express-session": "^1.14.1",
"file-size": "^1.0.0",
"fs-extra": "^4.0.1",
"fs-extra": "^7.0.0",
"gravatar": "^1.5.2",
"javascript-time-ago": "^1.0.30",
"js-yaml": "^3.9.0",
"jsondiffpatch": "0.2.4",
"jsonwebtoken": "^7.4.3",
"katex": "^0.10.0-rc.1",
"moemark-renderer": "^1.2.6",
"jsdom": "^13.0.0",
"jsondiffpatch": "0.3.11",
"jsonwebtoken": "^8.3.0",
"katex": "^0.10.0",
"mathjax-node": "^2.1.1",
"moemark": "^0.3.10",
"moment": "^2.15.0",
"msgpack-lite": "^0.1.26",
"multer": "^1.2.0",
"mysql": "^2.11.1",
"mysql2": "^1.6.2",
"node-7z": "^0.4.0",
"nodemailer": "^4.1.0",
"object-assign-deep": "^0.4.0",
"pygmentize-bundled-cached": "^1.1.0",
"randomstring": "^1.1.5",
"request": "^2.74.0",
"request-promise": "^4.1.1",
"sendmail": "^1.1.1",
"sequelize": "^3.24.3",
"sequelize": "^4.41.0",
"session-file-store": "^1.0.0",
"socket.io": "^2.0.3",
"sqlite3": "^3.1.4",
"syzoj-divine": "^1.0.2",
"tmp-promise": "^1.0.3",
"waliyun": "^3.1.1",
"winston": "^2.3.1",
"xss": "^0.3.3"
"winston": "^3.1.0",
"xss": "^1.0.3"
}
}

205
static/mathjax.css

@ -1,7 +1,6 @@
.MJX_Assistive_MathML {position: absolute!important; top: 0; left: 0; clip: rect(1px, 1px, 1px, 1px); padding: 1px 0 0 0!important; border: 0!important; height: 1px!important; width: 1px!important; overflow: hidden!important; display: block!important; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none}
.MJX_Assistive_MathML.MJX_Assistive_MathML_Block {width: 100%!important}
.mjx-chtml {display: inline-block; line-height: 0; text-indent: 0; text-align: left; text-transform: none; font-style: normal; font-weight: normal; font-size: 1.19em; font-size-adjust: none; letter-spacing: normal; word-wrap: normal; word-spacing: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0; min-height: 0; border: 0; margin: 0; padding: 1px 0}
.MJXc-display {display: block; text-align: center; margin: 1em 0; padding: 0; font-size: 1em !important; }
.mjx-chtml {display: inline-block; line-height: 0; text-indent: 0; text-align: left; text-transform: none; font-style: normal; font-weight: normal; font-size: 1.19em !important; font-size-adjust:
none; letter-spacing: normal; word-wrap: normal; word-spacing: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0; min-height: 0; border: 0; margin: 0; padding: 1px 0}
.MJXc-display {display: block; text-align: center; margin: 1em 0; padding: 0}
.mjx-chtml[tabindex]:focus, body :focus .mjx-chtml[tabindex] {display: inline-table}
.mjx-full-width {text-align: center; display: table-cell!important; width: 10000em}
.mjx-math {display: inline-block; border-collapse: separate; border-spacing: 0}
@ -45,20 +44,17 @@
.MJXc-space1 {margin-left: .167em}
.MJXc-space2 {margin-left: .222em}
.MJXc-space3 {margin-left: .278em}
.mjx-chartest {display: block; visibility: hidden; position: absolute; top: 0; line-height: normal; font-size: 500%}
.mjx-chartest .mjx-char {display: inline}
.mjx-chartest .mjx-box {padding-top: 1000px}
.MJXc-processed {display: none}
.mjx-test {display: block; font-style: normal; font-weight: normal; font-size: 100%; font-size-adjust: none; text-indent: 0; text-transform: none; letter-spacing: normal; word-spacing: normal; overflow: hidden; height: 1px}
.mjx-ex-box-test {position: absolute; width: 1px; height: 60ex}
.mjx-line-box-test {display: table!important}
.mjx-line-box-test span {display: table-cell!important; width: 10000em!important; min-width: 0; max-width: none; padding: 0; border: 0; margin: 0}
#MathJax_CHTML_Tooltip {background-color: InfoBackground; color: InfoText; border: 1px solid black; box-shadow: 2px 2px 5px #AAAAAA; -webkit-box-shadow: 2px 2px 5px #AAAAAA; -moz-box-shadow: 2px 2px 5px #AAAAAA; -khtml-box-shadow: 2px 2px 5px #AAAAAA; padding: 3px 4px; z-index: 401; position: absolute; left: 0; top: 0; width: auto; height: auto; display: none}
.mjx-chtml .mjx-noError {line-height: 1.2; vertical-align: ; font-size: 90%; text-align: left; color: black; padding: 1px 3px; border: 1px solid}
.MJXc-TeX-unknown-R {font-family: STIXGeneral,'Cambria Math','Arial Unicode MS',serif; font-style: normal; font-weight: normal}
.MJXc-TeX-unknown-I {font-family: STIXGeneral,'Cambria Math','Arial Unicode MS',serif; font-style: italic; font-weight: normal}
.MJXc-TeX-unknown-B {font-family: STIXGeneral,'Cambria Math','Arial Unicode MS',serif; font-style: normal; font-weight: bold}
.MJXc-TeX-unknown-BI {font-family: STIXGeneral,'Cambria Math','Arial Unicode MS',serif; font-style: italic; font-weight: bold}
.mjx-test.mjx-test-display {display: table!important}
.mjx-test.mjx-test-inline {display: inline!important; margin-right: -1px}
.mjx-test.mjx-test-default {display: block!important; clear: both}
.mjx-ex-box {display: inline-block!important; position: absolute; overflow: hidden; min-height: 0; max-height: none; padding: 0; border: 0; margin: 0; width: 1px; height: 60ex}
.mjx-test-inline .mjx-left-box {display: inline-block; width: 0; float: left}
.mjx-test-inline .mjx-right-box {display: inline-block; width: 0; float: right}
.mjx-test-display .mjx-right-box {display: table-cell!important; width: 10000em!important; min-width: 0; max-width: none; padding: 0; border: 0; margin: 0}
.MJXc-TeX-unknown-R {font-family: monospace; font-style: normal; font-weight: normal}
.MJXc-TeX-unknown-I {font-family: monospace; font-style: italic; font-weight: normal}
.MJXc-TeX-unknown-B {font-family: monospace; font-style: normal; font-weight: bold}
.MJXc-TeX-unknown-BI {font-family: monospace; font-style: italic; font-weight: bold}
.MJXc-TeX-ams-R {font-family: MJXc-TeX-ams-R,MJXc-TeX-ams-Rw}
.MJXc-TeX-cal-B {font-family: MJXc-TeX-cal-B,MJXc-TeX-cal-Bx,MJXc-TeX-cal-Bw}
.MJXc-TeX-frak-R {font-family: MJXc-TeX-frak-R,MJXc-TeX-frak-Rw}
@ -78,49 +74,184 @@
.MJXc-TeX-size2-R {font-family: MJXc-TeX-size2-R,MJXc-TeX-size2-Rw}
.MJXc-TeX-size3-R {font-family: MJXc-TeX-size3-R,MJXc-TeX-size3-Rw}
.MJXc-TeX-size4-R {font-family: MJXc-TeX-size4-R,MJXc-TeX-size4-Rw}
.MJXc-TeX-vec-R {font-family: MJXc-TeX-vec-R,MJXc-TeX-vec-Rw}
.MJXc-TeX-vec-B {font-family: MJXc-TeX-vec-B,MJXc-TeX-vec-Bx,MJXc-TeX-vec-Bw}
@font-face {font-family: MJXc-TeX-ams-R; src: local('MathJax_AMS'), local('MathJax_AMS-Regular')}
@font-face {font-family: MJXc-TeX-ams-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_AMS-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_AMS-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_AMS-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-ams-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_AMS-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_AMS-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_AMS-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-cal-B; src: local('MathJax_Caligraphic Bold'), local('MathJax_Caligraphic-Bold')}
@font-face {font-family: MJXc-TeX-cal-Bx; src: local('MathJax_Caligraphic'); font-weight: bold}
@font-face {font-family: MJXc-TeX-cal-Bw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Caligraphic-Bold.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Caligraphic-Bold.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Caligraphic-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-cal-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Caligraphic-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Caligraphic-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Caligraphic-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-frak-R; src: local('MathJax_Fraktur'), local('MathJax_Fraktur-Regular')}
@font-face {font-family: MJXc-TeX-frak-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Fraktur-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Fraktur-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Fraktur-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-frak-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Fraktur-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Fraktur-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Fraktur-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-frak-B; src: local('MathJax_Fraktur Bold'), local('MathJax_Fraktur-Bold')}
@font-face {font-family: MJXc-TeX-frak-Bx; src: local('MathJax_Fraktur'); font-weight: bold}
@font-face {font-family: MJXc-TeX-frak-Bw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Fraktur-Bold.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Fraktur-Bold.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Fraktur-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-frak-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Fraktur-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Fraktur-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Fraktur-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-math-BI; src: local('MathJax_Math BoldItalic'), local('MathJax_Math-BoldItalic')}
@font-face {font-family: MJXc-TeX-math-BIx; src: local('MathJax_Math'); font-weight: bold; font-style: italic}
@font-face {font-family: MJXc-TeX-math-BIw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Math-BoldItalic.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Math-BoldItalic.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Math-BoldItalic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-math-BIw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Math-BoldItalic.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Math-BoldItalic.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Math-BoldItalic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-R; src: local('MathJax_SansSerif'), local('MathJax_SansSerif-Regular')}
@font-face {font-family: MJXc-TeX-sans-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-B; src: local('MathJax_SansSerif Bold'), local('MathJax_SansSerif-Bold')}
@font-face {font-family: MJXc-TeX-sans-Bx; src: local('MathJax_SansSerif'); font-weight: bold}
@font-face {font-family: MJXc-TeX-sans-Bw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Bold.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Bold.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-I; src: local('MathJax_SansSerif Italic'), local('MathJax_SansSerif-Italic')}
@font-face {font-family: MJXc-TeX-sans-Ix; src: local('MathJax_SansSerif'); font-style: italic}
@font-face {font-family: MJXc-TeX-sans-Iw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Italic.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Italic.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-Iw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Italic.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Italic.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-script-R; src: local('MathJax_Script'), local('MathJax_Script-Regular')}
@font-face {font-family: MJXc-TeX-script-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Script-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Script-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Script-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-script-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Script-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Script-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Script-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-type-R; src: local('MathJax_Typewriter'), local('MathJax_Typewriter-Regular')}
@font-face {font-family: MJXc-TeX-type-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Typewriter-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Typewriter-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Typewriter-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-type-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Typewriter-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Typewriter-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Typewriter-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-cal-R; src: local('MathJax_Caligraphic'), local('MathJax_Caligraphic-Regular')}
@font-face {font-family: MJXc-TeX-cal-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Caligraphic-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Caligraphic-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Caligraphic-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-cal-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Caligraphic-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Caligraphic-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Caligraphic-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-B; src: local('MathJax_Main Bold'), local('MathJax_Main-Bold')}
@font-face {font-family: MJXc-TeX-main-Bx; src: local('MathJax_Main'); font-weight: bold}
@font-face {font-family: MJXc-TeX-main-Bw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Main-Bold.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Main-Bold.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Main-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Main-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Main-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Main-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-I; src: local('MathJax_Main Italic'), local('MathJax_Main-Italic')}
@font-face {font-family: MJXc-TeX-main-Ix; src: local('MathJax_Main'); font-style: italic}
@font-face {font-family: MJXc-TeX-main-Iw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Main-Italic.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Main-Italic.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Main-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-Iw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Main-Italic.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Main-Italic.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Main-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-R; src: local('MathJax_Main'), local('MathJax_Main-Regular')}
@font-face {font-family: MJXc-TeX-main-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Main-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Main-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Main-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Main-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Main-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Main-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-math-I; src: local('MathJax_Math Italic'), local('MathJax_Math-Italic')}
@font-face {font-family: MJXc-TeX-math-Ix; src: local('MathJax_Math'); font-style: italic}
@font-face {font-family: MJXc-TeX-math-Iw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Math-Italic.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Math-Italic.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Math-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-math-Iw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Math-Italic.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Math-Italic.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Math-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size1-R; src: local('MathJax_Size1'), local('MathJax_Size1-Regular')}
@font-face {font-family: MJXc-TeX-size1-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Size1-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Size1-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Size1-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size1-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Size1-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Size1-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Size1-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size2-R; src: local('MathJax_Size2'), local('MathJax_Size2-Regular')}
@font-face {font-family: MJXc-TeX-size2-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Size2-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Size2-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Size2-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size2-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Size2-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Size2-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Size2-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size3-R; src: local('MathJax_Size3'), local('MathJax_Size3-Regular')}
@font-face {font-family: MJXc-TeX-size3-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Size3-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Size3-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Size3-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size3-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Size3-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Size3-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Size3-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size4-R; src: local('MathJax_Size4'), local('MathJax_Size4-Regular')}
@font-face {font-family: MJXc-TeX-size4-Rw; src /*1*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/eot/MathJax_Size4-Regular.eot'); src /*2*/: url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/woff/MathJax_Size4-Regular.woff') format('woff'), url('https://cdn.loli.net/ajax/libs/mathjax/2.7.0/fonts/HTML-CSS/TeX/otf/MathJax_Size4-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size4-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Size4-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Size4-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Size4-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-vec-R; src: local('MathJax_Vector'), local('MathJax_Vector-Regular')}
@font-face {font-family: MJXc-TeX-vec-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Vector-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Vector-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Vector-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-vec-B; src: local('MathJax_Vector Bold'), local('MathJax_Vector-Bold')}
@font-face {font-family: MJXc-TeX-vec-Bx; src: local('MathJax_Vector'); font-weight: bold}
@font-face {font-family: MJXc-TeX-vec-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Vector-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Vector-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Vector-Bold.otf') format('opentype')}.mjx-chtml {display: inline-block; line-height: 0; text-indent: 0; text-align: left; text-transform: none; font-style: normal; font-weight: normal; font-size: 100%; font-size-adjust:
none; letter-spacing: normal; word-wrap: normal; word-spacing: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0; min-height: 0; border: 0; margin: 0; padding: 1px 0}
.MJXc-display {display: block; text-align: center; margin: 1em 0; padding: 0}
.mjx-chtml[tabindex]:focus, body :focus .mjx-chtml[tabindex] {display: inline-table}
.mjx-full-width {text-align: center; display: table-cell!important; width: 10000em}
.mjx-math {display: inline-block; border-collapse: separate; border-spacing: 0}
.mjx-math * {display: inline-block; -webkit-box-sizing: content-box!important; -moz-box-sizing: content-box!important; box-sizing: content-box!important; text-align: left}
.mjx-numerator {display: block; text-align: center}
.mjx-denominator {display: block; text-align: center}
.MJXc-stacked {height: 0; position: relative}
.MJXc-stacked > * {position: absolute}
.MJXc-bevelled > * {display: inline-block}
.mjx-stack {display: inline-block}
.mjx-op {display: block}
.mjx-under {display: table-cell}
.mjx-over {display: block}
.mjx-over > * {padding-left: 0px!important; padding-right: 0px!important}
.mjx-under > * {padding-left: 0px!important; padding-right: 0px!important}
.mjx-stack > .mjx-sup {display: block}
.mjx-stack > .mjx-sub {display: block}
.mjx-prestack > .mjx-presup {display: block}
.mjx-prestack > .mjx-presub {display: block}
.mjx-delim-h > .mjx-char {display: inline-block}
.mjx-surd {vertical-align: top}
.mjx-mphantom * {visibility: hidden}
.mjx-merror {background-color: #FFFF88; color: #CC0000; border: 1px solid #CC0000; padding: 2px 3px; font-style: normal; font-size: 90%}
.mjx-annotation-xml {line-height: normal}
.mjx-menclose > svg {fill: none; stroke: currentColor}
.mjx-mtr {display: table-row}
.mjx-mlabeledtr {display: table-row}
.mjx-mtd {display: table-cell; text-align: center}
.mjx-label {display: table-row}
.mjx-box {display: inline-block}
.mjx-block {display: block}
.mjx-span {display: inline}
.mjx-char {display: block; white-space: pre}
.mjx-itable {display: inline-table; width: auto}
.mjx-row {display: table-row}
.mjx-cell {display: table-cell}
.mjx-table {display: table; width: 100%}
.mjx-line {display: block; height: 0}
.mjx-strut {width: 0; padding-top: 1em}
.mjx-vsize {width: 0}
.MJXc-space1 {margin-left: .167em}
.MJXc-space2 {margin-left: .222em}
.MJXc-space3 {margin-left: .278em}
.mjx-test.mjx-test-display {display: table!important}
.mjx-test.mjx-test-inline {display: inline!important; margin-right: -1px}
.mjx-test.mjx-test-default {display: block!important; clear: both}
.mjx-ex-box {display: inline-block!important; position: absolute; overflow: hidden; min-height: 0; max-height: none; padding: 0; border: 0; margin: 0; width: 1px; height: 60ex}
.mjx-test-inline .mjx-left-box {display: inline-block; width: 0; float: left}
.mjx-test-inline .mjx-right-box {display: inline-block; width: 0; float: right}
.mjx-test-display .mjx-right-box {display: table-cell!important; width: 10000em!important; min-width: 0; max-width: none; padding: 0; border: 0; margin: 0}
.MJXc-TeX-unknown-R {font-family: monospace; font-style: normal; font-weight: normal}
.MJXc-TeX-unknown-I {font-family: monospace; font-style: italic; font-weight: normal}
.MJXc-TeX-unknown-B {font-family: monospace; font-style: normal; font-weight: bold}
.MJXc-TeX-unknown-BI {font-family: monospace; font-style: italic; font-weight: bold}
.MJXc-TeX-ams-R {font-family: MJXc-TeX-ams-R,MJXc-TeX-ams-Rw}
.MJXc-TeX-cal-B {font-family: MJXc-TeX-cal-B,MJXc-TeX-cal-Bx,MJXc-TeX-cal-Bw}
.MJXc-TeX-frak-R {font-family: MJXc-TeX-frak-R,MJXc-TeX-frak-Rw}
.MJXc-TeX-frak-B {font-family: MJXc-TeX-frak-B,MJXc-TeX-frak-Bx,MJXc-TeX-frak-Bw}
.MJXc-TeX-math-BI {font-family: MJXc-TeX-math-BI,MJXc-TeX-math-BIx,MJXc-TeX-math-BIw}
.MJXc-TeX-sans-R {font-family: MJXc-TeX-sans-R,MJXc-TeX-sans-Rw}
.MJXc-TeX-sans-B {font-family: MJXc-TeX-sans-B,MJXc-TeX-sans-Bx,MJXc-TeX-sans-Bw}
.MJXc-TeX-sans-I {font-family: MJXc-TeX-sans-I,MJXc-TeX-sans-Ix,MJXc-TeX-sans-Iw}
.MJXc-TeX-script-R {font-family: MJXc-TeX-script-R,MJXc-TeX-script-Rw}
.MJXc-TeX-type-R {font-family: MJXc-TeX-type-R,MJXc-TeX-type-Rw}
.MJXc-TeX-cal-R {font-family: MJXc-TeX-cal-R,MJXc-TeX-cal-Rw}
.MJXc-TeX-main-B {font-family: MJXc-TeX-main-B,MJXc-TeX-main-Bx,MJXc-TeX-main-Bw}
.MJXc-TeX-main-I {font-family: MJXc-TeX-main-I,MJXc-TeX-main-Ix,MJXc-TeX-main-Iw}
.MJXc-TeX-main-R {font-family: MJXc-TeX-main-R,MJXc-TeX-main-Rw}
.MJXc-TeX-math-I {font-family: MJXc-TeX-math-I,MJXc-TeX-math-Ix,MJXc-TeX-math-Iw}
.MJXc-TeX-size1-R {font-family: MJXc-TeX-size1-R,MJXc-TeX-size1-Rw}
.MJXc-TeX-size2-R {font-family: MJXc-TeX-size2-R,MJXc-TeX-size2-Rw}
.MJXc-TeX-size3-R {font-family: MJXc-TeX-size3-R,MJXc-TeX-size3-Rw}
.MJXc-TeX-size4-R {font-family: MJXc-TeX-size4-R,MJXc-TeX-size4-Rw}
.MJXc-TeX-vec-R {font-family: MJXc-TeX-vec-R,MJXc-TeX-vec-Rw}
.MJXc-TeX-vec-B {font-family: MJXc-TeX-vec-B,MJXc-TeX-vec-Bx,MJXc-TeX-vec-Bw}
@font-face {font-family: MJXc-TeX-ams-R; src: local('MathJax_AMS'), local('MathJax_AMS-Regular')}
@font-face {font-family: MJXc-TeX-ams-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_AMS-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_AMS-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_AMS-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-cal-B; src: local('MathJax_Caligraphic Bold'), local('MathJax_Caligraphic-Bold')}
@font-face {font-family: MJXc-TeX-cal-Bx; src: local('MathJax_Caligraphic'); font-weight: bold}
@font-face {font-family: MJXc-TeX-cal-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Caligraphic-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Caligraphic-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Caligraphic-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-frak-R; src: local('MathJax_Fraktur'), local('MathJax_Fraktur-Regular')}
@font-face {font-family: MJXc-TeX-frak-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Fraktur-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Fraktur-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Fraktur-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-frak-B; src: local('MathJax_Fraktur Bold'), local('MathJax_Fraktur-Bold')}
@font-face {font-family: MJXc-TeX-frak-Bx; src: local('MathJax_Fraktur'); font-weight: bold}
@font-face {font-family: MJXc-TeX-frak-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Fraktur-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Fraktur-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Fraktur-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-math-BI; src: local('MathJax_Math BoldItalic'), local('MathJax_Math-BoldItalic')}
@font-face {font-family: MJXc-TeX-math-BIx; src: local('MathJax_Math'); font-weight: bold; font-style: italic}
@font-face {font-family: MJXc-TeX-math-BIw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Math-BoldItalic.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Math-BoldItalic.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Math-BoldItalic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-R; src: local('MathJax_SansSerif'), local('MathJax_SansSerif-Regular')}
@font-face {font-family: MJXc-TeX-sans-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-B; src: local('MathJax_SansSerif Bold'), local('MathJax_SansSerif-Bold')}
@font-face {font-family: MJXc-TeX-sans-Bx; src: local('MathJax_SansSerif'); font-weight: bold}
@font-face {font-family: MJXc-TeX-sans-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-sans-I; src: local('MathJax_SansSerif Italic'), local('MathJax_SansSerif-Italic')}
@font-face {font-family: MJXc-TeX-sans-Ix; src: local('MathJax_SansSerif'); font-style: italic}
@font-face {font-family: MJXc-TeX-sans-Iw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_SansSerif-Italic.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_SansSerif-Italic.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_SansSerif-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-script-R; src: local('MathJax_Script'), local('MathJax_Script-Regular')}
@font-face {font-family: MJXc-TeX-script-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Script-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Script-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Script-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-type-R; src: local('MathJax_Typewriter'), local('MathJax_Typewriter-Regular')}
@font-face {font-family: MJXc-TeX-type-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Typewriter-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Typewriter-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Typewriter-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-cal-R; src: local('MathJax_Caligraphic'), local('MathJax_Caligraphic-Regular')}
@font-face {font-family: MJXc-TeX-cal-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Caligraphic-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Caligraphic-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Caligraphic-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-B; src: local('MathJax_Main Bold'), local('MathJax_Main-Bold')}
@font-face {font-family: MJXc-TeX-main-Bx; src: local('MathJax_Main'); font-weight: bold}
@font-face {font-family: MJXc-TeX-main-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Main-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Main-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Main-Bold.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-I; src: local('MathJax_Main Italic'), local('MathJax_Main-Italic')}
@font-face {font-family: MJXc-TeX-main-Ix; src: local('MathJax_Main'); font-style: italic}
@font-face {font-family: MJXc-TeX-main-Iw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Main-Italic.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Main-Italic.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Main-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-main-R; src: local('MathJax_Main'), local('MathJax_Main-Regular')}
@font-face {font-family: MJXc-TeX-main-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Main-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Main-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Main-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-math-I; src: local('MathJax_Math Italic'), local('MathJax_Math-Italic')}
@font-face {font-family: MJXc-TeX-math-Ix; src: local('MathJax_Math'); font-style: italic}
@font-face {font-family: MJXc-TeX-math-Iw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Math-Italic.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Math-Italic.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Math-Italic.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size1-R; src: local('MathJax_Size1'), local('MathJax_Size1-Regular')}
@font-face {font-family: MJXc-TeX-size1-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Size1-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Size1-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Size1-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size2-R; src: local('MathJax_Size2'), local('MathJax_Size2-Regular')}
@font-face {font-family: MJXc-TeX-size2-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Size2-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Size2-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Size2-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size3-R; src: local('MathJax_Size3'), local('MathJax_Size3-Regular')}
@font-face {font-family: MJXc-TeX-size3-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Size3-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Size3-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Size3-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-size4-R; src: local('MathJax_Size4'), local('MathJax_Size4-Regular')}
@font-face {font-family: MJXc-TeX-size4-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Size4-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Size4-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Size4-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-vec-R; src: local('MathJax_Vector'), local('MathJax_Vector-Regular')}
@font-face {font-family: MJXc-TeX-vec-Rw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Vector-Regular.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Vector-Regular.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Vector-Regular.otf') format('opentype')}
@font-face {font-family: MJXc-TeX-vec-B; src: local('MathJax_Vector Bold'), local('MathJax_Vector-Bold')}
@font-face {font-family: MJXc-TeX-vec-Bx; src: local('MathJax_Vector'); font-weight: bold}
@font-face {font-family: MJXc-TeX-vec-Bw; src /*1*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/eot/MathJax_Vector-Bold.eot'); src /*2*/: url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/woff/MathJax_Vector-Bold.woff') format('woff'), url('https://cdnjs.loli.net/ajax/libs/mathjax/2.7.5/fonts/HTML-CSS/TeX/otf/MathJax_Vector-Bold.otf') format('opentype')

52
static/style.css

@ -35,6 +35,14 @@ body
sans-serif;
}
a.black-link {
color: #000;
}
a.black-link:hover {
color: #4183c4;
}
.font-content {
font-family: 'Open Sans', 'Source Han Sans SC', 'Noto Sans CJK SC', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
}
@ -43,10 +51,6 @@ body
font-family: 'Fira Mono', 'Noto Sans CJK SC', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft Yahei', monospace;
}
.main.container {
margin-top: 5.5em;
}
.padding {
padding-left: 1em;
padding-right: 1em;
@ -92,14 +96,6 @@ body > .ui.page.dimmer {
position: fixed !important;
}
/* status color */
/*
.accordion .status_detail {
transition: color .1s ease;
}
*/
:not(.status_detail).status.success,
.title:hover .status_detail.status.success,
.title.active .status_detail.status.success,
@ -272,3 +268,35 @@ code {
animation: spinner-icon-rotate 3s linear infinite;
display: block;
}
::-webkit-scrollbar {
-webkit-appearance: none;
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 0px;
}
::-webkit-scrollbar-thumb {
cursor: pointer;
border-radius: 5px;
background: rgba(0, 0, 0, 0.25);
-webkit-transition: color 0.2s ease;
transition: color 0.2s ease;
}
::-webkit-scrollbar-thumb:window-inactive {
background: rgba(0, 0, 0, 0.15);
}
::-webkit-scrollbar-thumb:hover {
background: rgba(128, 135, 139, 0.8);
}
.ui.top.attached.block.header i.icon {
font-size: 1em;
vertical-align: initial;
}

57
utility.js

@ -1,24 +1,3 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
Array.prototype.forEachAsync = Array.prototype.mapAsync = async function (fn) {
return Promise.all(this.map(fn));
};
@ -40,41 +19,14 @@ let Promise = require('bluebird');
let path = require('path');
let fs = Promise.promisifyAll(require('fs-extra'));
let util = require('util');
let renderer = require('moemark-renderer');
let markdownRenderer = require('./libs/markdown');
let moment = require('moment');
let url = require('url');
let querystring = require('querystring');
let pygmentize = require('pygmentize-bundled-cached');
let gravatar = require('gravatar');
let filesize = require('file-size');
let AsyncLock = require('async-lock');
function escapeHTML(s) {
// Code from http://stackoverflow.com/questions/5251520/how-do-i-escape-some-html-in-javascript/5251551
return s.replace(/[^0-9A-Za-z ]/g, (c) => {
return "&#" + c.charCodeAt(0) + ";";
});
}
function highlightPygmentize(code, lang, cb) {
pygmentize({
lang: lang,
format: 'html',
options: {
nowrap: true,
classprefix: 'pl-'
}
}, code, (err, res) => {
if (err || res.toString() === 'undefined') {
cb(escapeHTML(code));
} else {
cb(res);
}
});
}
renderer.config.highlight = highlightPygmentize;
module.exports = {
resolvePath(s) {
let a = Array.from(arguments);
@ -137,13 +89,13 @@ module.exports = {
return new Promise((resolve, reject) => {
if (!keys) {
if (!obj || !obj.trim()) resolve("");
else renderer(obj, { mathjaxUseHtml: true }, s => {
else markdownRenderer(obj, s => {
resolve(replaceUI(replaceXSS(s)));
});
} else {
let res = obj, cnt = keys.length;
for (let key of keys) {
renderer(res[key], { mathjaxUseHtml: true }, (s) => {
markdownRenderer(res[key], (s) => {
res[key] = replaceUI(replaceXSS(s));
if (!--cnt) resolve(res);
});
@ -206,10 +158,9 @@ module.exports = {
if (encoded) res += '?' + encoded;
return res;
},
escapeHTML: escapeHTML,
highlight(code, lang) {
return new Promise((resolve, reject) => {
highlightPygmentize(code, lang, res => {
require('./libs/highlight')(code, lang, res => {
resolve(res);
});
});

2
views/admin_config.ejs

@ -48,7 +48,7 @@ for (let item in items) {
}
%>
<div style="text-align: center; ">
<button class="ui blue button">提交</button>
<button class="ui blue labeled icon button"><i class="ui edit icon"></i>提交</button>
</div>
</form>
<% include admin_footer %>

2
views/admin_header.ejs

@ -5,7 +5,7 @@ let items = {
privilege: '权限管理',
rejudge: '一键重测',
links: '友链管理',
rating: 'Rating 管理',
rating: '积分管理',
raw: '配置文件',
other: '其他操作'
};

2
views/admin_info.ejs

@ -17,6 +17,8 @@
}
.statistic .label {
padding-top: 15px;
font-size: 1.4em !important;
font-weight: normal !important;
}
</style>

2
views/admin_links.ejs

@ -54,7 +54,7 @@ this.adminPage = 'links';
<input type="text" id="add_url">
</div>
</div>
<div style="text-align: center; "><div id="add_button" class="ui submit button">添加</a></div>
<div style="text-align: center; "><div id="add_button" class="ui submit blue labeled icon button"><i class="ui add icon"></i>添加</a></div>
</div>
<form method="post" id="submit_form">

6
views/admin_other.ejs

@ -1,8 +1,8 @@
<% this.adminPage = 'other'; %>
<% include admin_header %>
<form method="post" class="ui form">
<p><button class="ui blue button" name="type" value="reset_count" type="submit">重新计算提交及 AC 数</button></p>
<p><button class="ui blue button" name="type" value="reset_discussion" type="submit">重新计算讨论版回复数和回复时间</button></p>
<p><button class="ui blue button" name="type" value="reset_codelen" type="submit">重新计算提交代码长度</button></p>
<p><button class="ui blue labeled icon button" name="type" value="reset_count" type="submit"><i class="ui repeat icon"></i>重新计算提交及 AC 数</button></p>
<p><button class="ui blue labeled icon button" name="type" value="reset_discussion" type="submit"><i class="ui repeat icon"></i>重新计算讨论版回复数和回复时间</button></p>
<p><button class="ui blue labeled icon button" name="type" value="reset_codelen" type="submit"><i class="ui repeat icon"></i>重新计算提交代码长度</button></p>
</form>
<% include admin_footer %>

2
views/admin_privilege.ejs

@ -37,7 +37,7 @@ let privileges = {
</table>
<div style="text-align: center; ">
<button class="ui button" onclick="submit()">提交</button>
<button class="ui blue labeled icon button" onclick="submit()"><i class="ui edit icon"></i>提交</button>
</div>
<form method="post" id="submit_form">

4
views/admin_rating.ejs

@ -14,10 +14,10 @@
</div>
</div>
</div>
<button class="ui blue button" name="type" value="doit" type="submit">计算此比赛的 Rating</button>
<button class="ui blue labeled icon button" name="type" value="doit" type="submit"><i class="ui calculator icon"></i>计算此比赛的积分</button>
</form>
注意:如果删除一个比赛的 Rating,则该比赛之上的所有比赛也将被删除,Rating 将还原至该比赛之前的状态!
注意:如果删除一个比赛的积分,则该比赛之上的所有比赛也将被删除,积分将还原至该比赛之前的状态!
<form action="<%= syzoj.utils.makeUrl(['admin', 'rating', 'delete']) %>" method="POST">
<div class="ui relaxed divided list">
<% for (const calc of calcs) { %>

2
views/admin_raw.ejs

@ -28,7 +28,7 @@ editor.getSession().on("change", function () {
<div style="text-align: center; ">
<button class="ui blue button">提交</button>
<button class="ui blue labeled icon button"><i class="ui edit icon"></i>提交</button>
</div>
</form>
<% include admin_footer %>

8
views/admin_rejudge.ejs

@ -71,15 +71,15 @@
</div>
<div style="text-align: center; ">
<button class="ui button" name="type" value="query">查询</button>
<button class="ui labeled icon button" name="type" value="query"><i class="ui search icon"></i>查询</button>
<% if (form.type === 'rejudge') { %>
<button class="ui blue disabled button" name="type" value="rejudge">已重测 <%= count %> 条记录</button>
<button class="ui blue disabled labeled icon button" name="type" value="rejudge"><i class="ui checkmark icon"></i>已重测 <%= count %> 条记录</button>
<% } else if (count !== null) { %>
<a onclick="$('#modal-rejudge').modal('show');" class="ui blue<% if (count === 0) { %> disabled<% } %> button"><% if (count === 0) { %>没有符合条件的记录<% } else { %>重测 <%= count %> 条记录<% } %></a>
<a onclick="$('#modal-rejudge').modal('show');" class="ui blue<% if (count === 0) { %> disabled<% } %> labeled icon button"><i class="ui repeat icon"></i><% if (count === 0) { %>没有符合条件的记录<% } else { %>重测 <%= count %> 条记录<% } %></a>
<button id="submit_rejudge" name="type" value="rejudge" style="display: none; "></button>
<div class="ui basic modal" id="modal-rejudge">
<div class="ui icon header">
<i class="refresh icon"></i>
<i class="retweet icon"></i>
<p style="margin-top: 15px; ">重新评测</p>
</div>
<div class="content" style="text-align: center; ">

15
views/article.ejs

@ -18,12 +18,15 @@
<% } %>
</div>
<h1><%= article.title %></h1>
<p style="font_size: 0.7em"><img style="vertical-align: middle;" src="<%= syzoj.utils.gravatar(article.user.email, 64) %>" width="32" height="32">
<a href="<%= syzoj.utils.makeUrl(['user', article.user_id]) %>"><%= article.user.username %></a><% if (article.user.nameplate) { %><%- article.user.nameplate %><% } %> 于 <%= syzoj.utils.formatDate(article.public_time) %> 发表,<%= syzoj.utils.formatDate(article.update_time) %> 最后更新
<p style="margin-bottom: -5px; ">
<img style="vertical-align: middle; margin-bottom: 2px; margin-right: 2px; " src="<%= syzoj.utils.gravatar(article.user.email, 34) %>" width="17" height="17">
<b style="margin-right: 30px; "><a class="black-link" href="<%= syzoj.utils.makeUrl(['user', article.user_id]) %>"><%= article.user.username %></a><% if (article.user.nameplate) { %><%- article.user.nameplate %><% } %></b>
<b style="margin-right: 30px; "><i class="calendar icon"></i> <%= syzoj.utils.formatDate(article.public_time) %></b>
<% if (article.public_time !== article.update_time) { %><b style="margin-right: 30px; "><i class="edit icon"></i> <%= syzoj.utils.formatDate(article.update_time) %></b><% } %>
<% if (article.allowedEdit) { %>
<a class="ui mini red button" onclick="$('#modal-delete').modal('show')">删除文章</a>
<a class="ui mini button" href="<%= syzoj.utils.makeUrl(['article', article.id, 'edit']) %>">编辑文章</a>
<div class="ui basic modal" id="modal-delete">
<a style="margin-top: -4px; " class="ui mini right floated labeled icon button" href="<%= syzoj.utils.makeUrl(['article', article.id, 'edit']) %>"><i class="ui edit icon"></i>编辑</a>
<a style="margin-top: -4px; margin-right: 3px; " class="ui mini red right floated labeled icon button" onclick="$('#modal-delete').modal('show')"><i class="ui trash icon"></i>删除</a>
<div class="ui basic modal right floated" id="modal-delete">
<div class="ui icon header">
<i class="trash icon"></i>
<p style="margin-top: 15px; ">删除文章</p>
@ -96,9 +99,11 @@
<div class="field">
<textarea name="comment"></textarea>
</div>
<div style="text-align: center; ">
<button type="submit" class="ui labeled submit icon button">
<i class="icon edit"></i> 回复
</button>
</div>
</form>
<% } %>
</div>

3
views/article_edit.ejs

@ -15,6 +15,7 @@
<div class="field">
<label for="title">标题</label>
<input type="text" id="title" name="title" value="<%= article.title %>">
<div style="margin-top: 15px; "></div>
<label for="content">内容</label>
<textarea rows="15" id="content" name="content" class="markdown-edit"><%= article.content %></textarea>
<% if (user && user.is_admin) { %>
@ -30,9 +31,11 @@
<div class="ui header" id="pv-title"></div>
<div id="pv-content" class="font-content"></div>
</div>
<div style="text-align: center; ">
<button type="submit" class="ui labeled submit icon button">
<i class="icon edit"></i> 提交
</button>
</div>
</form>
</div>

2
views/contest_edit.ejs

@ -77,7 +77,7 @@
<label><span style="visibility: hidden; "> </span></label>
</div>
</div>
<button type="submit" class="ui button">提交</button>
<div style="text-align: center; "><button type="submit" class="ui labeled icon blue button"><i class="ui edit icon"></i>提交</button></div>
</form>
<script>
$(function () {

9
views/contest_ranklist.ejs

@ -154,5 +154,14 @@
<% } %>
</tbody>
</table>
<% if (!ranklist.length) { %>
<div style="background-color: #fff; height: 18px; margin-top: -18px; "></div>
<div class="ui placeholder segment" style="margin-top: 0px; ">
<div class="ui icon header">
<i class="ui file icon" style="margin-bottom: 20px; "></i>
暂无选手提交
</div>
</div>
<% } %>
</div>
<% include footer %>

30
views/contests.ejs

@ -1,14 +1,16 @@
<% this.title = '比赛' %>
<% include header %>
<div class="padding">
<% if (contests.length) { %>
<% if (user && user.is_admin) { %>
<div class="ui grid">
<div class="row">
<div class="column">
<a href="<%= syzoj.utils.makeUrl(['contest', 0, 'edit']) %>" class="ui mini right floated button">添加比赛</a>
</div>
</div>
</div>
<form class="ui mini form">
<div class="inline fields" style="margin-bottom: 25px; white-space: nowrap; ">
<a href="<%= syzoj.utils.makeUrl(['contest', 0, 'edit']) %>" class="ui mini labeled icon right floated button" style="margin-left: auto; ">
<i class="ui icon write"></i>
添加比赛
</a>
</div>
</form>
<% } %>
<table class="ui very basic center aligned table">
<thead>
@ -42,6 +44,20 @@
<% } %>
</tbody>
</table>
<% } else { %>
<div class="ui placeholder segment">
<div class="ui icon header">
<i class="calendar icon" style="margin-bottom: 20px; "></i>
暂无比赛
</div>
<% if (user && user.is_admin) { %>
<a href="<%= syzoj.utils.makeUrl(['contest', 0, 'edit']) %>" class="ui primary labeled icon button">
<i class="ui icon write"></i>
添加第一场比赛
</a>
<% } %>
</div>
<% } %>
<br>
<% include page %>
</div>

23
views/discussion.ejs

@ -18,8 +18,8 @@
<% } %>
</div>
</div>
<div class="six wide right aligned column">
<% if(in_problems) { %>
<div class="six wide right aligned column" style="margin-bottom: 10px; ">
<% if (in_problems) { %>
<a style="margin-left: 10px; " href="<%= syzoj.utils.makeUrl(['discussion', 'global']) %>" class="ui labeled icon mini blue button">
<i class="world icon"></i>
全局板块
@ -36,14 +36,17 @@
题目板块
</a>
<% } %>
<% if (articles.length && user) { %>
<a style="margin-left: 10px; " href="<%= syzoj.utils.makeUrl(['article', 0, 'edit'], problem ? { problem_id: problem.id } : null) %>" class="ui labeled icon mini button">
<i class="write icon"></i>
发帖
</a>
<% } %>
<% } %>
</div>
</div>
</div>
<% if (articles.length) { %>
<table class="ui very basic center aligned table">
<thead>
<tr>
@ -71,6 +74,20 @@
</tbody>
</table>
<br>
<% include page %>
<% include page %>
<% } else { %>
<div class="ui placeholder segment">
<div class="ui icon header">
<i class="chat icon" style="margin-bottom: 20px; "></i>
暂无讨论
</div>
<% if (!in_problems && user) { %>
<a href="<%= syzoj.utils.makeUrl(['article', 0, 'edit'], problem ? { problem_id: problem.id } : null) %>" class="ui primary labeled icon button">
<i class="write icon"></i>
发帖
</a>
<% } %>
</div>
<% } %>
</div>
<% include footer %>

6
views/footer.ejs

@ -1,11 +1,11 @@
</div>
<div class="ui vertical footer segment">
<div class="ui vertical footer segment" style="margin-top: 10px; ">
<div class="ui center aligned container">
<span style="color: #999;"><%= syzoj.config.title %> Powered by <a href="https://github.com/syzoj/syzoj" target="_blank">SYZOJ</a>.</span>
</div>
</div>
<script src="https://cdnjs.loli.net/ajax/libs/semantic-ui/2.3.3/semantic.min.js"></script>
</div>
<script src="https://cdnjs.loli.net/ajax/libs/semantic-ui/2.4.1/semantic.min.js"></script>
<script src="https://cdnjs.loli.net/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
<script src="/script.js?20170710"></script>
</body>

27
views/header.ejs

@ -1,21 +1,21 @@
<% include util %>
<!DOCTYPE html>
<html lang="zh-CN">
<html lang="zh-CN" style="position: fixed; width: 100%; overflow: hidden; ">
<head>
<meta charset="utf-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta name="viewport" content="width=1200">
<title><%= title %> - <%= syzoj.config.title %></title>
<link href="https://cdnjs.loli.net/ajax/libs/semantic-ui/2.3.3/semantic.min.css" rel="stylesheet">
<link href="https://cdnjs.loli.net/ajax/libs/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
<link href="/tomorrow.css" rel="stylesheet">
<link href="/mathjax.css" rel="stylesheet">
<link href="https://cdnjs.loli.net/ajax/libs/KaTeX/0.10.0-rc.1/katex.min.css" rel="stylesheet">
<link href="/mathjax.css?20181105" rel="stylesheet">
<link href="https://cdnjs.loli.net/ajax/libs/KaTeX/0.10.0/katex.min.css" rel="stylesheet">
<link href="https://cdnjs.loli.net/ajax/libs/morris.js/0.5.1/morris.css" rel="stylesheet">
<link href="/style.css?20181023" rel="stylesheet">
<link href="/style.css?20181108" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Fira+Mono" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Lato:400,700,400italic,700italic&subset=latin" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i&amp;subset=latin-ext" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Raleway:300" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Exo+2:600" rel="stylesheet">
<script src="https://cdnjs.loli.net/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<% if (syzoj.config.google_analytics && syzoj.config.google_analytics !== 'UA-XXXXXXXX-X') { %>
<script>
@ -30,10 +30,18 @@
</script>
<% } %>
</head>
<body>
<div class="ui fixed borderless menu" style="position: fixed; ">
<body style="position: relative; margin-top: 49px; height: calc(100% - 49px); overflow-y: overlay; ">
<div class="ui fixed borderless menu" style="position: fixed; height: 49px; ">
<div class="ui container">
<a class="header item" href="/"><span style="font-family: Raleway; font-size: 1.5em; font-weight: 300; "><%= syzoj.config.title %></span></a>
<%
if (syzoj.config.logo.url) {
let width = syzoj.config.logo.width ? syzoj.config.logo.width + 'px' : 'auto';
let height = syzoj.config.logo.height ? syzoj.config.logo.height + 'px' : 'auto';
%>
<a class="header item" href="/"><img src="<%= syzoj.config.logo.url %>" style="width: <%= width %>; height: <%= height %>; "></a>
<% } else { %>
<a class="header item" href="/"><span style="font-family: 'Exo 2'; font-size: 1.5em; font-weight: 600; "><%= syzoj.config.title %></span></a>
<% } %>
<a class="item<% if (active === '') { %> active<% } %>" href="/"><i class="home icon"></i> 首页</a>
<a class="item<% if (active.startsWith('problem')) { %> active<% } %>" href="/problems"><i class="list icon"></i> 题库</a>
<a class="item<% if (active.startsWith('contest')) { %> active<% } %>" href="/contests"><i class="calendar icon"></i> 比赛</a>
@ -71,4 +79,5 @@
</div>
</div>
</div>
<div style="margin-top: 28px; ">
<div class="ui main container">

41
views/index.ejs

@ -3,7 +3,7 @@
<div class="padding">
<div class="ui three column grid">
<div class="eleven wide column">
<h4 class="ui top attached block header">公告</h4>
<h4 class="ui top attached block header"><i class="ui info icon"></i>公告</h4>
<div class="ui bottom attached segment">
<table class="ui very basic table">
<thead>
@ -22,7 +22,7 @@
</tbody>
</table>
</div>
<h4 class="ui top attached block header">排名</h4>
<h4 class="ui top attached block header"><i class="ui signal icon"></i>排名</h4>
<div class="ui bottom attached segment">
<table class="ui very basic center aligned table" style="table-layout: fixed; ">
<thead>
@ -51,7 +51,7 @@
++i;
%>
<tr>
<td><%= i %></td>
<td><b><%= i %></b></td>
<td><a href="<%= syzoj.utils.makeUrl(['user', user.id]) %>"><%= user.username %></a><% if (user.nameplate) { %><%- user.nameplate %><% } %></td>
<td style="font-content">
<script id="user-infomation-script-<%= i %>">
@ -76,7 +76,7 @@
</div>
</div>
<div class="right floated five wide column">
<h4 class="ui top attached block header">一言(ヒトコト)</h4>
<h4 class="ui top attached block header"><i class="ui quote left icon"></i>一言(ヒトコト)</h4>
<div class="ui bottom attached center aligned segment">
<div class="ui active centered inline loader" id="hitokoto-loader"></div>
<script>
@ -92,14 +92,35 @@
<div style="font-size: 1em; line-height: 1.5em; display: none; " id="hitokoto-content"></div>
<div style="text-align: right; margin-top: 15px; font-size: 0.9em; color: #666; display: none; " id="hitokoto-from"></div>
</div>
<h4 class="ui top attached block header"><i class="ui rss icon"></i>最近更新</h4>
<div class="ui bottom attached segment">
<table class="ui very basic center aligned table">
<thead>
<tr>
<th width="70%">题目</th>
<th width="30%">更新时间</th>
</tr>
</thead>
<tbody>
<%
for (let problem of problems) {
%>
<tr>
<td><a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>"><%= problem.title %></a></td>
<td><%= problem.time %></td>
</tr>
<% } %>
</tbody>
</table>
</div>
<%
if (fortune) {
let color;
if (fortune.fortune.indexOf('吉') != -1) color = '#1fb752';
if (fortune.fortune.indexOf('吉') != -1) color = '#0ccf00';
else if (fortune.fortune.indexOf('凶') != -1) color = '#f25e65';
else color = '#444';
%>
<h4 class="ui top attached block header">今日运势</h4>
<h4 class="ui top attached block header"><i class="ui magic icon"></i>今日运势</h4>
<div class="ui bottom attached segment">
<div style="height: 15px; "></div>
<div class="ui two column center aligned padded grid">
@ -110,7 +131,7 @@
</div>
<div class="two column row">
<div class="column">
<div style="color: #1fb752; ">
<div style="color: #0ccf00; ">
<% if (fortune.good.length) { %>
<strong>宜:</strong><%= fortune.good[0].title %>
<br>
@ -145,7 +166,7 @@
</div>
</div>
<% } %>
<h4 class="ui top attached block header">搜索题目</h4>
<h4 class="ui top attached block header"><i class="ui search icon"></i>搜索题目</h4>
<div class="ui bottom attached segment">
<form action="<%= syzoj.utils.makeUrl(['problems', 'search']) %>" method="get">
<div class="ui search" style="width: 100%; ">
@ -157,7 +178,7 @@
</div>
</form>
</div>
<h4 class="ui top attached block header">近期比赛</h4>
<h4 class="ui top attached block header"><i class="ui calendar icon"></i>近期比赛</h4>
<div class="ui bottom attached <% if (!contests || !contests.length) { %>center aligned <% } %>segment">
<% if (!contests || !contests.length) { %>
还没有举行过任何比赛
@ -192,7 +213,7 @@
<% } %>
</div>
<% if (typeof links !== 'undefined' && links && links.length) { %>
<h4 class="ui top attached block header font-content">友情链接</h4>
<h4 class="ui top attached block header font-content"><i class="ui linkify icon"></i>友情链接</h4>
<div class="ui bottom attached segment">
<ul style="margin: 0; padding-left: 20px; ">
<% for (let link of links) { %>

4
views/login.ejs

@ -6,7 +6,7 @@
<div class="row">
<div class="column" style="max-width: 450px">
<h2 class="ui image header">
<div class="content">
<div class="content" style="margin-bottom: 10px; ">
登录
</div>
</h2>
@ -32,7 +32,7 @@
</form>
<div class="ui message">
<a href="/sign_up">注册</a>
<a href="/sign_up">注册账号</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href="<%= syzoj.utils.makeUrl(['forget']) %>">忘记密码</a>
</div>
</div>

2
views/page.ejs

@ -1,4 +1,4 @@
<% if (paginate.pageCnt) { %>
<% if (paginate.pageCnt > 1) { %>
<div style="text-align: center; ">
<div class="ui pagination menu" style="box-shadow: none; ">
<a class="<% if (paginate.currPage === 1) { %> disabled<% } %> icon item" <% if (paginate.currPage !== 1) { %>href="<%= syzoj.utils.makeUrl(req, Object.assign({}, req.query, { page: paginate.currPage - 1 })) %>" <% } %>id="page_prev">

4
views/problem.ejs

@ -302,7 +302,7 @@ div[class*=ace_br] {
</div>
</div>
</div>
<div class="ui center aligned vertical segment" style="padding-bottom: 0; "><button type="submit" class="ui button">提交</button></div>
<div class="ui center aligned vertical segment" style="padding-bottom: 0; "><button type="submit" class="ui labeled icon button"><i class="ui edit icon"></i>提交</button></div>
<% } else { %>
<input name="language" type="hidden" id="form">
<input name="code" type="hidden">
@ -334,7 +334,7 @@ div[class*=ace_br] {
</div>
</div>
<div class="ui center aligned vertical segment" style="padding-bottom: 0; ">
<button type="submit" class="ui button">提交</button>
<button type="submit" class="ui labeled icon button"><i class="ui edit icon"></i>提交</button>
<!--div onclick="show_custom_test()" class="ui positive button">自定义测试</div-->
</div>
<% } %>

4
views/problem_data.ejs

@ -117,8 +117,8 @@ function getIcon(filename) {
<label for="answer">上传文件(可一次性上传多个)</label>
<input type="file" name="file" multiple id="upload_file">
<div class="ui center aligned vertical segment" style="padding-bottom: 0; ">
<div class="ui button" onclick="check_replace()">提交</div>
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a>
<div class="ui labeled icon blue button" onclick="check_replace()"><i class="ui edit icon"></i>提交</div>
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui labeled icon button"><i class="ui angle left icon"></i>返回</a>
</div>
</div>
</div>

6
views/problem_edit.ejs

@ -64,7 +64,11 @@
<div class="font-content" id="pv-hint"></div>
</div>
<% if (problem.allowedEdit) { %>
<button type="submit" class="ui button">提交</button>
<div style="text-align: center; ">
<button type="submit" class="ui labeled submit icon button">
<i class="icon edit"></i> 提交
</button>
</div>
<% } %>
</form>
</div>

6
views/problem_import.ejs

@ -17,6 +17,10 @@
<label>题目链接</label>
<input type="text" name="url" placeholder="如:http://example.com/problem/1">
</div>
<button type="submit" class="ui button">提交</button>
<div style="text-align: center; ">
<button type="submit" class="ui labeled submit icon button">
<i class="icon edit"></i> 提交
</button>
</div>
</form>
<% include footer %>

8
views/problem_manage.ejs

@ -95,8 +95,8 @@
<label for="additional_file">上传附加文件(请使用 ZIP 格式)</label>
<input type="file" id="additional_file" name="additional_file">
</div>
<button type="submit" class="ui button">提交</button>
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a>
<button type="submit" class="ui labeled icon blue button"><i class="ui edit icon"></i>提交</button>
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui labeled icon button"><i class="ui angle left icon"></i>返回</a>
</form>
</div>
</div>
@ -145,7 +145,9 @@ $(function () {
});
function checkSubmit() {
;
if (!$('#file-io-output-name').val()) {
$('#file-io-output-name').val($('#file-io-output-name').attr('placeholder'));
}
}
</script>
<% include footer %>

6
views/problem_tag_edit.ejs

@ -60,7 +60,11 @@
</div>
</div>
</div>
<button type="submit" class="ui button">提交</button>
<div style="text-align: center; ">
<button type="submit" class="ui labeled submit icon button">
<i class="icon edit"></i> 提交
</button>
</div>
</form>
<script>
$(function () {

19
views/problems.ejs

@ -25,7 +25,7 @@ if (typeof tags !== 'undefined') tagIDs = tags.map(x => x.id);
<form action="<%= syzoj.utils.makeUrl(['problems', 'search']) %>" method="get">
<div class="ui search" style="width: 280px; height: 28px; margin-top: -5.3px; ">
<div class="ui left icon input" style="width: 100%; ">
<input class="prompt" style="width: 100%; " type="text" placeholder="ID / 题目名 …" name="keyword">
<input class="prompt" style="width: 100%; " type="text" value="<%= req.query.keyword %>" placeholder="ID / 题目名 …" name="keyword">
<i class="search icon"></i>
</div>
<div class="results" style="width: 100%; "></div>
@ -81,10 +81,10 @@ if (typeof tags !== 'undefined') tagIDs = tags.map(x => x.id);
</div>
</div>
<% if (problems.length) { %>
<div style="margin-bottom: 30px; ">
<% include page %>
</div>
<table class="ui very basic center aligned table">
<thead>
<tr>
@ -113,7 +113,7 @@ if (typeof tags !== 'undefined') tagIDs = tags.map(x => x.id);
<% } %>
</td>
<% } %>
<td><%= problem.id %></td>
<td><b><%= problem.id %></b></td>
<td class="left aligned">
<a style="vertical-align: middle; " href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>"><%= problem.title %>
<% if (!problem.is_public) { %><span class="ui header"><span class="ui tiny red label">未公开</span></span><% } %>
@ -149,6 +149,19 @@ if (typeof tags !== 'undefined') tagIDs = tags.map(x => x.id);
</tbody>
</table><br>
<% include page %>
<% } else { %>
<div class="ui placeholder segment">
<div class="ui icon header">
<% if (typeof req.query.keyword !== 'undefined') { %>
<i class="search icon" style="margin-bottom: 20px; "></i>
找不到符合条件的题目
<% } else { %>
<i class="list icon" style="margin-bottom: 20px; "></i>
暂无题目
<% } %>
</div>
</div>
<% } %>
<script>
document.addEventListener('keydown', function (event) {
if (event.keyCode === 39) document.getElementById('page_next').click();

4
views/ranklist.ejs

@ -15,7 +15,7 @@
<th style="width: 150px; "><%- createSortableTitle('username', '用户名', true) %></th>
<th>个性签名</th>
<th style="width: 100px; "><%- createSortableTitle('ac_num', '通过数量', false) %></th>
<th style="width: 100px; "><%- createSortableTitle('rating', 'Rating', false) %></th>
<th style="width: 100px; "><%- createSortableTitle('rating', '积分', false) %></th>
</tr>
</thead>
<script>
@ -37,7 +37,7 @@
++i;
%>
<tr>
<td><%= i %></td>
<td><b><%= i %></b></td>
<td><a href="<%= syzoj.utils.makeUrl(['user', user.id]) %>"><%= user.username %></a><% if (user.nameplate) { %><%- user.nameplate %><% } %></td>
<td class="font-content">
<script id="user-infomation-script-<%= i %>">

4
views/status_label.ejs

@ -20,7 +20,7 @@ const iconList = {
};
Vue.component('status-label', {
template: '#statusIconTemplate',
props: ['status'],
props: ['status', 'indetail'],
computed: {
icon() {
if (this.status in iconList) {
@ -30,7 +30,7 @@ Vue.component('status-label', {
}
},
colorClass() {
return this.status.toLowerCase().split(' ').join('_');
return (this.indetail ? 'status_detail ' : '') + this.status.toLowerCase().split(' ').join('_');
}
}
})

4
views/submission.ejs

@ -64,7 +64,7 @@
子任务 #{{ $index + 1 }}
</div>
<div class="four wide column">
<status-label :status="getSubtaskResult(subtask)"></status-label>
<status-label :status="getSubtaskResult(subtask)" :indetail="true"></status-label>
</div>
<div class="three wide column" v-if="subtask.score != null">
得分:<span style="font-weight: normal; ">{{ Math.trunc(subtask.score) }}</span>
@ -81,7 +81,7 @@
测试点 #{{ $caseIndex + 1 }}
</div>
<div class="four wide column">
<status-label :status="getTestcaseStatus(curCase)"></status-label>
<status-label :status="getTestcaseStatus(curCase)" :indetail="true"></status-label>
</div>
<template v-if="checkTestcaseOK(curCase)">
<div class="three wide column">

68
views/submissions_item.ejs

@ -50,28 +50,76 @@ Vue.component('submission-item', {
<script id="submissionItemTemplate" type="text/x-template">
<tr>
<td><a :href="submissionLink">#{{ data.info.submissionId }}</a></td>
<td ref="problemLabel"><a ref="problemLabelTextFit" style="width: 230px; height: 22px; display: block; margin: 0 auto; line-height: 22px;" :href="problemLink">#{{ config.inContest ? alpha(data.info.problemId) : data.info.problemId }}. {{ data.info.problemName }}</a></td>
<td><a :href="submissionLink"><status-label :status="statusString"></status-label></a></td>
<% if (active === 'submissions') { %>
<td><a :href="submissionLink"><b>#{{ data.info.submissionId }}</b></a></td>
<% } else { %>
<td><b>#{{ data.info.submissionId }}</b></td>
<% } %>
<td ref="problemLabel"><a ref="problemLabelTextFit" style="width: 230px; height: 22px; display: block; margin: 0 auto; line-height: 22px;" :href="problemLink"><b>#{{ config.inContest ? alpha(data.info.problemId) : data.info.problemId }}.</b> {{ data.info.problemName }}</a></td>
<% if (active === 'submissions') { %>
<td><a :href="submissionLink"><b><status-label :status="statusString"></status-label></b></a></td>
<% } else { %>
<td><b><status-label :status="statusString"></status-label></b></td>
<% } %>
<template v-if="data.result">
<td v-if="config.showScore"><a :href="submissionLink"><span class="score" :class="scoreClass">{{ (data.result.score != null && data.result.score !== NaN) ? Math.floor(data.result.score) : '' }}</span></a></td>
<td v-if="config.showUsage">{{ (data.result.time != null && data.result.time !== NaN) ? data.result.time.toString() + ' ms' : '' }}</td>
<td v-if="config.showUsage">{{ (data.result.memory != null && data.result.memory !== NaN) ? data.result.memory.toString() + ' KiB' : '' }}</td>
<% if (active === 'submissions') { %>
<td v-if="config.showScore"><a :href="submissionLink"><span class="score" :class="scoreClass">{{ Math.floor(data.result.score || 0) }}</span></a></td>
<% } else { %>
<td v-if="config.showScore"><span class="score" :class="scoreClass">{{ Math.floor(data.result.score || 0) }}</span></td>
<% } %>
<td v-if="config.showUsage">{{ (data.result.time || 0).toString() + ' ms' }}</td>
<td v-if="config.showUsage">{{ (data.result.memory || 0).toString() + ' KiB' }}</td>
</template> <template v-else>
<td v-if="config.showScore"/> <td v-if="config.showUsage"/> <td v-if="config.showUsage"/>
</template>
<td v-if="config.showCode">
<span v-if="data.info.language"><a :href="submissionLink">{{ data.info.language }}</a> / </span>
<% if (active === 'submissions') { %>
<span v-if="data.info.language"><a :href="submissionLink"><b>{{ data.info.language }}</b></a> / </span>
<% } else { %>
<span v-if="data.info.language"><b>{{ data.info.language }}</b> / </span>
<% } %>
{{ data.info.codeSize }}
</td>
<td><a :href="userLink">{{ data.info.user }}</a></td>
<td>{{ data.info.submitTime }}</td>
<td v-if="showRejudge">
<form :action="submissionLink + '/rejudge'" method="POST">
<button type="submit" style="color: #000; padding: 0; border: none; background: none;" value="rejudge"><i class="repeat icon"></i></button>
</form>
<a id="rejudge-button" :onclick="'check_rejudge(' + (statusString === 'Waiting' || statusString.startsWith('Running')).toString() + ')'" style="color: #000; " href="#"><i class="repeat icon"></i></a>
<% if (active === 'submission') { %>
<div class="ui basic modal" id="modal-rejudge">
<div class="ui icon header">
<i class="retweet icon"></i>
<p style="margin-top: 15px; ">重新评测</p>
</div>
<div class="content" style="text-align: center; ">
<p>确认重新评测该提交记录吗?</p>
<p id="warning_pending"><strong>警告:只有管理员可以重新评测一个未评测完成的记录,<br>这种情况一般发生在评测服务中断后,如果一个提交正在被评测,<br>则将其重新评测会导致系统错乱!</strong></p>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
</div>
<a class="ui green ok inverted button" :href-post="submissionLink + '/rejudge'">
<i class="checkmark icon"></i>
</a>
</div>
</div>
<% } %>
</td>
</tr>
</script>
<% if (active === 'submission') { %>
<script>
function check_rejudge(pending) {
if (pending) {
$('#warning_pending').css('display', '');
} else {
$('#warning_pending').css('display', 'none');
}
$('#modal-rejudge').modal('show');
}
</script>
<% } %>

4
views/user.ejs

@ -32,7 +32,7 @@
</div>
<div class="extra content">
<a><i class="check icon"></i>通过 <%= show_user.ac_problems.length %> 题</a>
<a><i class="star icon"></i>Rating <%= show_user.rating %></a>
<a style="float: right; "><i class="star icon"></i>积分 <%= show_user.rating %></a>
</div>
</div>
@ -135,7 +135,7 @@
<tr>
<th>比赛</th>
<th>名次</th>
<th>Rating</th>
<th>积分</th>
</tr>
</thead>
<tbody>

8
views/user_edit.ejs

@ -70,9 +70,9 @@
<label>管理用户</label>
</div>
</div>
<div style="text-align: center; ">
<button type="submit" class="ui button">修改</button>
<a href="<%= syzoj.utils.makeUrl(['user', edited_user.id]) %>" class="ui blue button">返回个人资料</a>
<div style="text-align: center; margin-top: 30px; ">
<button type="submit" class="ui blue labeled icon button"><i class="ui icon edit"></i>修改</button>
<a href="<%= syzoj.utils.makeUrl(['user', edited_user.id]) %>" class="ui labeled icon button"><i class="ui icon angle left"></i>返回</a>
</div>
</form>
</div>
@ -119,7 +119,7 @@ function check() {
</script>
<script>
$(function () {
$('.ui.dropdown').dropdown();
$('.ui.dropdown:not(.simple)').dropdown();
});
</script>
<% include footer %>

Loading…
Cancel
Save