Browse Source

Finish monaco editor

master
Menci 6 years ago
parent
commit
89660b162b
  1. 18
      language-config.json
  2. 41
      static/self/monaco-editor.css
  3. 352
      static/self/monaco-editor.js
  4. 8
      views/admin_raw.ejs
  5. 20
      views/article.ejs
  6. 34
      views/article_edit.ejs
  7. 33
      views/contest_edit.ejs
  8. 1
      views/header.ejs
  9. 15
      views/monaco-editor.ejs
  10. 22
      views/problem.ejs
  11. 82
      views/problem_edit.ejs
  12. 27
      views/user_edit.ejs

18
language-config.json

@ -3,63 +3,63 @@
"show": "C++",
"highlight": "cpp",
"version": "GCC 8.2.0",
"editor": "c_cpp",
"editor": "cpp",
"format": "cpp"
},
"cpp11": {
"show": "C++ 11",
"highlight": "cpp",
"version": "GCC 8.2.0",
"editor": "c_cpp",
"editor": "cpp",
"format": "cpp"
},
"cpp17": {
"show": "C++ 17",
"highlight": "cpp",
"version": "GCC 8.2.0",
"editor": "c_cpp",
"editor": "cpp",
"format": "cpp"
},
"cpp-noilinux": {
"show": "C++ (NOI)",
"highlight": "cpp",
"version": "GCC 4.8.4 (NOILinux 1.4.1)",
"editor": "c_cpp",
"editor": "cpp",
"format": "cpp"
},
"cpp11-noilinux": {
"show": "C++ 11 (NOI)",
"highlight": "cpp",
"version": "GCC 4.8.4 (NOILinux 1.4.1)",
"editor": "c_cpp",
"editor": "cpp",
"format": "cpp"
},
"cpp11-clang": {
"show": "C++ 11 (Clang)",
"highlight": "cpp",
"version": "Clang 7.0.1",
"editor": "c_cpp",
"editor": "cpp",
"format": "cpp"
},
"cpp17-clang": {
"show": "C++ 17 (Clang)",
"highlight": "cpp",
"version": "Clang 7.0.1",
"editor": "c_cpp",
"editor": "cpp",
"format": "cpp"
},
"c": {
"show": "C",
"highlight": "c",
"version": "Clang 7.0.1",
"editor": "c_cpp",
"editor": "c",
"format": "c"
},
"c-noilinux": {
"show": "C (NOI)",
"highlight": "c",
"version": "GCC 4.8.4 (NOILinux 1.4.1)",
"editor": "c_cpp",
"editor": "c",
"format": "c"
},
"csharp": {

41
static/self/monaco-editor.css

@ -1,6 +1,26 @@
.editor {
background: #fff;
font-family: "Fira Mono", "Bitstream Vera Sans Mono", Menlo, Consolas, "Lucida Console", monospace;
position: relative;
border: 1px solid rgba(34, 36, 38, 0.15);
}
.editor.editor-with-border {
border-radius: .28571429rem;
overflow: hidden;
outline: none;
}
.editor.editor-with-border:focus-within {
border-color: #85b7d9;
}
.editor.editor-with-border .monaco-editor .margin {
visibility: hidden;
}
.editor.editor-with-border .monaco-editor .lines-content.monaco-editor-background {
margin-top: 9px;
margin-left: 4px;
}
.monaco-editor .inputarea {
@ -29,9 +49,22 @@
}
.monaco-editor .line-numbers {
color: #4d4d4c !important;
color: #575756 !important;
}
.monaco-editor .margin-view-overlays .current-line ~ .line-numbers {
color: #000 !important;
font-weight: 500;
}
.editor.editor-with-border .monaco-editor .monaco-scrollable-element .scrollbar.vertical {
background: rgba(0,0,0,.1);
border-radius: 0;
}
.monaco-editor .margin-view-overlays .current-line {
background-color: #efefef !important;
.editor.editor-with-border .monaco-editor .monaco-scrollable-element .scrollbar.vertical .slider {
border-radius: 5px;
background: rgba(0,0,0,.25);
-webkit-transition: color .2s ease;
transition: color .2s ease;
}

352
static/self/monaco-editor.js

@ -33,9 +33,34 @@ require(['vs/editor/editor.main'], function () {
PascalDefinition,
PythonDefinition,
RubyDefinition) {
monaco.languages.register({ id: 'ace_c_cpp' });
MonacoAceTokenizer.registerRulesForLanguage('ace_c_cpp', new c_cppDefinition.default);
monaco.languages.setLanguageConfiguration('ace_c_cpp', {
var overrideLangauges = [
'cpp',
'c',
'csharp',
'haskell',
'java',
'javascript',
'pascal',
'python',
'ruby',
'markdown'
];
monaco.languages.getLanguages().forEach(function (lang) {
if (overrideLangauges.includes(lang.id)) {
lang.loader = function () {
return { then: function () {} };
};
}
});
var cppAliases = ['c', 'cpp', 'c++', 'cxx', 'cc'];
for (var i in cppAliases) {
var alias = cppAliases[i];
monaco.languages.register({ id: alias });
MonacoAceTokenizer.registerRulesForLanguage(alias, new c_cppDefinition.default);
monaco.languages.setLanguageConfiguration(alias, {
comments: {
lineComment: '//',
blockComment: ['/*', '*/'],
@ -66,10 +91,15 @@ require(['vs/editor/editor.main'], function () {
}
}
});
}
monaco.languages.register({ id: 'ace_csharp' });
MonacoAceTokenizer.registerRulesForLanguage('ace_csharp', new CSharpDefinition.default);
monaco.languages.setLanguageConfiguration('ace_csharp', {
var csharpAliases = ['csharp', 'cs', 'c#'];
for (var i in csharpAliases) {
var alias = csharpAliases[i];
monaco.languages.register({ id: alias });
MonacoAceTokenizer.registerRulesForLanguage(alias, new CSharpDefinition.default);
monaco.languages.setLanguageConfiguration(alias, {
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
lineComment: '//',
@ -102,10 +132,11 @@ require(['vs/editor/editor.main'], function () {
}
}
});
}
monaco.languages.register({ id: 'ace_haskell' });
MonacoAceTokenizer.registerRulesForLanguage('ace_haskell', new HaskellDefinition.default);
monaco.languages.setLanguageConfiguration('ace_haskell', {
monaco.languages.register({ id: 'haskell' });
MonacoAceTokenizer.registerRulesForLanguage('haskell', new HaskellDefinition.default);
monaco.languages.setLanguageConfiguration('haskell', {
comments: {
lineComment: '--',
blockComment: ['{-', '-}']
@ -136,9 +167,9 @@ require(['vs/editor/editor.main'], function () {
}
});
monaco.languages.register({ id: 'ace_java' });
MonacoAceTokenizer.registerRulesForLanguage('ace_java', new JavaDefinition.default);
monaco.languages.setLanguageConfiguration('ace_java', {
monaco.languages.register({ id: 'java' });
MonacoAceTokenizer.registerRulesForLanguage('java', new JavaDefinition.default);
monaco.languages.setLanguageConfiguration('java', {
// the default separators except `@$`
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
@ -173,9 +204,12 @@ require(['vs/editor/editor.main'], function () {
}
});
monaco.languages.register({ id: 'ace_javascript' });
MonacoAceTokenizer.registerRulesForLanguage('ace_javascript', new JavaScriptDefinition.default);
monaco.languages.setLanguageConfiguration('ace_javascript', {
var javascriptAliases = ['javascript', 'js'];
for (var i in javascriptAliases) {
var alias = javascriptAliases[i];
monaco.languages.register({ id: alias });
MonacoAceTokenizer.registerRulesForLanguage(alias, new JavaScriptDefinition.default);
monaco.languages.setLanguageConfiguration(alias, {
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
lineComment: '//',
@ -225,10 +259,15 @@ require(['vs/editor/editor.main'], function () {
}
}
});
}
monaco.languages.register({ id: 'ace_pascal' });
MonacoAceTokenizer.registerRulesForLanguage('ace_pascal', new PascalDefinition.default);
monaco.languages.setLanguageConfiguration('ace_pascal', {
var pascalAliases = ['pascal', 'pas'];
for (var i in pascalAliases) {
var alias = pascalAliases[i];
monaco.languages.register({ id: alias });
MonacoAceTokenizer.registerRulesForLanguage(alias, new PascalDefinition.default);
monaco.languages.setLanguageConfiguration(alias, {
// the default separators except `@$`
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
@ -262,10 +301,15 @@ require(['vs/editor/editor.main'], function () {
}
}
});
}
var pythonAliases = ['python', 'python2', 'python3', 'py', 'py2', 'py3'];
for (var i in pythonAliases) {
var alias = pythonAliases[i];
monaco.languages.register({ id: 'ace_python' });
MonacoAceTokenizer.registerRulesForLanguage('ace_python', new PythonDefinition.default);
monaco.languages.setLanguageConfiguration('ace_python', {
monaco.languages.register({ id: alias });
MonacoAceTokenizer.registerRulesForLanguage(alias, new PythonDefinition.default);
monaco.languages.setLanguageConfiguration(alias, {
comments: {
lineComment: '#',
blockComment: ['\'\'\'', '\'\'\''],
@ -303,10 +347,11 @@ require(['vs/editor/editor.main'], function () {
}
}
});
}
monaco.languages.register({ id: 'ace_ruby' });
MonacoAceTokenizer.registerRulesForLanguage('ace_ruby', new RubyDefinition.default);
monaco.languages.setLanguageConfiguration('ace_ruby', {
monaco.languages.register({ id: 'ruby' });
MonacoAceTokenizer.registerRulesForLanguage('ruby', new RubyDefinition.default);
monaco.languages.setLanguageConfiguration('ruby', {
comments: {
lineComment: '#',
blockComment: ['=begin', '=end'],
@ -336,9 +381,212 @@ require(['vs/editor/editor.main'], function () {
}
});
var markdownAliases = ['markdown', 'md'];
for (var i in markdownAliases) {
var alias = markdownAliases[i];
monaco.languages.register({ id: alias });
monaco.languages.setLanguageConfiguration(alias, {
comments: {
blockComment: ['<!--', '-->',]
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '<', close: '>', notIn: ['string'] }
],
surroundingPairs: [
{ open: '(', close: ')' },
{ open: '[', close: ']' },
{ open: '`', close: '`' },
],
folding: {
markers: {
start: new RegExp("^\\s*<!--\\s*#?region\\b.*-->"),
end: new RegExp("^\\s*<!--\\s*#?endregion\\b.*-->")
}
}
});
monaco.languages.setMonarchTokensProvider(alias, {
defaultToken: '',
tokenPostfix: '.md',
// escape codes
control: /[\\`*_\[\]{}()#+\-\.!\$]/,
noncontrol: /[^\\`*_\[\]{}()#+\-\.!\$]/,
escapes: /\\(?:@control)/,
// escape codes for javascript/CSS strings
jsescapes: /\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,
// non matched elements
empty: [
'area', 'base', 'basefont', 'br', 'col', 'frame',
'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'
],
tokenizer: {
root: [
// headers (with #)
[/^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/, ['white', 'keyword', 'keyword', 'keyword']],
// headers (with =)
[/^\s*(=+|\-+)\s*$/, 'keyword'],
// headers (with ***)
[/^\s*((\*[ ]?)+)\s*$/, 'meta.separator'],
// quote
[/^\s*>+/, 'comment'],
// list (starting with * or number)
[/^\s*([\*\-+:]|\d+\.)\s/, 'keyword'],
// code block (4 spaces indent)
[/^(\t|[ ]{4})[^ ].*$/, 'string'],
// code block (3 tilde)
[/^\s*~~~\s*((?:\w|[\/\-#])+)?\s*$/, { token: 'string', next: '@codeblock' }],
// display math
[/\$\$/, { token: 'string', next: '@displaymath' }],
// github style code blocks (with backticks and language)
[/^\s*```\s*((?:\w|[\/\-#\+])+)\s*$/, { token: 'string', next: '@codeblockgh', nextEmbedded: '$1' }],
// github style code blocks (with backticks but no language)
[/^\s*```\s*$/, { token: 'string', next: '@codeblock' }],
// markup within lines
{ include: '@linecontent' },
],
displaymath: [
[/\\\$/, 'variable.source'],
[/\$\$/, { token: 'string', next: '@pop' }],
[/./, 'variable.source']
],
codeblock: [
[/^\s*~~~\s*$/, { token: 'string', next: '@pop' }],
[/^\s*```\s*$/, { token: 'string', next: '@pop' }],
[/.*$/, 'variable.source'],
],
// github style code blocks
codeblockgh: [
[/```\s*$/, { token: 'variable.source', next: '@pop', nextEmbedded: '@pop' }],
[/[^`]+/, 'variable.source'],
],
linecontent: [
// inline math
[/\$/, { token: 'string', next: '@inlinemath' }],
// escapes
[/&\w+;/, 'string.escape'],
[/@escapes/, 'escape'],
// various markup
[/\b__([^\\_]|@escapes|_(?!_))+__\b/, 'strong'],
[/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/, 'strong'],
[/\b_[^_]+_\b/, 'emphasis'],
[/\*([^\\*]|@escapes)+\*/, 'emphasis'],
[/`([^\\`]|@escapes)+`/, 'variable'],
// links
[/\{+[^}]+\}+/, 'string.target'],
[/(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/, ['string.link', '', 'string.link']],
[/(!?\[)((?:[^\]\\]|@escapes)*)(\])/, 'string.link'],
// or html
{ include: 'html' },
],
inlinemath: [
[/\\\$/, 'variable.source'],
[/\$/, { token: 'string', next: '@pop' }],
[/./, 'variable.source']
],
// Note: it is tempting to rather switch to the real HTML mode instead of building our own here
// but currently there is a limitation in Monarch that prevents us from doing it: The opening
// '<' would start the HTML mode, however there is no way to jump 1 character back to let the
// HTML mode also tokenize the opening angle bracket. Thus, even though we could jump to HTML,
// we cannot correctly tokenize it in that mode yet.
html: [
// html tags
[/<(\w+)\/>/, 'tag'],
[/<(\w+)/, {
cases: {
'@empty': { token: 'tag', next: '@tag.$1' },
'@default': { token: 'tag', next: '@tag.$1' }
}
}],
[/<\/(\w+)\s*>/, { token: 'tag' }],
[/<!--/, 'comment', '@comment']
],
comment: [
[/[^<\-]+/, 'comment.content'],
[/-->/, 'comment', '@pop'],
[/<!--/, 'comment.content.invalid'],
[/[<\-]/, 'comment.content']
],
// Almost full HTML tag matching, complete with embedded scripts & styles
tag: [
[/[ \t\r\n]+/, 'white'],
[/(type)(\s*=\s*)(")([^"]+)(")/, ['attribute.name.html', 'delimiter.html', 'string.html',
{ token: 'string.html', switchTo: '@tag.$S2.$4' },
'string.html']],
[/(type)(\s*=\s*)(')([^']+)(')/, ['attribute.name.html', 'delimiter.html', 'string.html',
{ token: 'string.html', switchTo: '@tag.$S2.$4' },
'string.html']],
[/(\w+)(\s*=\s*)("[^"]*"|'[^']*')/, ['attribute.name.html', 'delimiter.html', 'string.html']],
[/\w+/, 'attribute.name.html'],
[/\/>/, 'tag', '@pop'],
[/>/, {
cases: {
'$S2==style': { token: 'tag', switchTo: 'embeddedStyle', nextEmbedded: 'text/css' },
'$S2==script': {
cases: {
'$S3': { token: 'tag', switchTo: 'embeddedScript', nextEmbedded: '$S3' },
'@default': { token: 'tag', switchTo: 'embeddedScript', nextEmbedded: 'text/javascript' }
}
},
'@default': { token: 'tag', next: '@pop' }
}
}],
],
embeddedStyle: [
[/[^<]+/, ''],
[/<\/style\s*>/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
[/</, '']
],
embeddedScript: [
[/[^<]+/, ''],
[/<\/script\s*>/, { token: '@rematch', next: '@pop', nextEmbedded: '@pop' }],
[/</, '']
],
}
});
}
$.getScript(window.pathSelfLib + "monaco-editor-tomorrow.js", function () {
window.createEditor = function (editorElement, langauge, content) {
window.createCodeEditor = function (editorElement, langauge, content) {
editorElement.innerHTML = '';
return monaco.editor.create(editorElement, {
value: content,
language: langauge,
@ -347,12 +595,62 @@ require(['vs/editor/editor.main'], function () {
theme: 'tomorrow',
lineHeight: 22,
fontSize: 14,
fontFamily: "'Fira Mono', 'Bitstream Vera Sans Mono', 'Menlo', 'Consolas', 'Lucida Console', monospace",
fontFamily: "'Fira Mono', 'Bitstream Vera Sans Mono', 'Menlo', 'Consolas', 'Lucida Console', 'Source Han Sans SC', 'Noto Sans CJK SC', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft Yahei', monospace",
lineNumbersMinChars: 4,
glyphMargin: false
glyphMargin: false,
renderFinalNewline: true,
scrollbar: {
useShadows: false,
verticalScrollbarSize: 0,
vertical: 'hidden'
},
overviewRulerBorder: false,
hideCursorInOverviewRuler: true
});
};
window.createMarkdownEditor = function (editorElement, content, input) {
editorElement.innerHTML = '';
var editor = monaco.editor.create(editorElement, {
value: content,
language: 'markdown',
multicursorModifier: 'ctrlCmd',
cursorWidth: 1,
theme: 'tomorrow',
fontSize: 14,
fontFamily: "'Fira Mono', 'Source Han Sans SC', 'Noto Sans CJK SC', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft Yahei', monospace",
lineNumbersMinChars: 4,
glyphMargin: false,
lineNumbers: false,
folding: false,
minimap: {
enabled: false
},
hover: {
enabled: false
},
wordWrap: "on",
renderIndentGuides: false,
renderFinalNewline: false,
wordBasedSuggestions: false,
renderLineHighlight: false,
occurrencesHighlight: false,
scrollbar: {
useShadows: false,
vertical: 'auto',
verticalScrollbarSize: 10
},
overviewRulerBorder: false,
hideCursorInOverviewRuler: true
});
input.form.addEventListener('submit', function () {
input.value = editor.getValue();
});
return editor;
};
window.editorLoaded = true;
for (var i in window.editorLoadedHandles) {
window.editorLoadedHandles[i]();

8
views/admin_raw.ejs

@ -2,10 +2,12 @@
<% include admin_header %>
<form method="post">
<div id="editor" class="editor" style="border: 1px solid #D4D4D5; height: 500px; margin-bottom: 20px; "></div>
<% include monaco-editor %>
<div id="editor" class="editor" style="border: 1px solid #D4D4D5; height: 500px; margin-bottom: 20px; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="data">
<% include monaco-editor %>
<script type="text/javascript">
var originalData = <%- serializejs(data) %>;
var input = $('[name="data"]');
@ -13,7 +15,7 @@ input.val(originalData);
window.onEditorLoaded(function () {
var editor;
window.editor = editor = window.createEditor(document.getElementById("editor"), "json", originalData);
window.editor = editor = window.createCodeEditor(document.getElementById("editor"), "json", originalData);
$('form').on('submit', function () {
input.val(editor.getValue());
});

20
views/article.ejs

@ -95,16 +95,32 @@
</div>
<% } %>
<% if (article.allowedComment) { %>
<% include monaco-editor %>
<form class="ui reply form" method="post" action="<%= syzoj.utils.makeUrl(['article', article.id, 'comment']) %>">
<div class="field">
<textarea name="comment"></textarea>
<div id="comment" class="editor editor-with-border" style="height: 200px; width: 100%; margin-bottom: 1em; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="comment">
<div style="text-align: center; ">
<button type="submit" class="ui labeled submit icon button">
<i class="icon edit"></i> 回复
</button>
</div>
</form>
<script>
var editors = {
comment: { defaultValue: '' }
};
window.onEditorLoaded(function () {
for (var name in editors) {
var editor = editors[name];
var editorElement = document.getElementById(name);
var input = document.getElementsByName(name)[0];
editor.editor = window.createMarkdownEditor(editorElement, editor.defaultValue, input);
}
});
</script>
<% } %>
</div>
<% include footer %>

34
views/article_edit.ejs

@ -1,23 +1,32 @@
<% this.title = article.id ? '编辑帖子' : '发表帖子'; %>
<% include header %>
<% include monaco-editor %>
<style type="text/css">
.am-panel-title{
font-size: 1.6em;
}
</style>
<div class="padding">
<form class="ui form" method="post">
<form class="" method="post">
<div class="ui top attached tabular menu">
<a class="item active" data-tab="edit">编辑</a>
<a class="item" data-tab="preview" id="preview_tab">预览</a>
</div>
<div class="ui bottom attached tab segment active" data-tab="edit">
<div class="ui form">
<div class="field">
<label for="title">标题</label>
<input type="text" id="title" name="title" value="<%= article.title %>">
<div style="margin-top: 15px; "></div>
</div>
<div class="field">
<label for="content">内容</label>
<textarea rows="15" id="content" name="content" class="markdown-edit"><%= article.content %></textarea>
</div>
</div>
<div class="editor editor-with-border" id="content" style="width: 100%; height: 400px; margin-bottom: 1em; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="content">
<% if (user && user.is_admin) { %>
<div class="ui <% if (article.is_notice) { %>checked <% } %>checkbox" style="margin-top: 15px; ">
<input <% if (article.is_notice) { %>checked=""<% } %> name="is_notice" type="checkbox">
@ -26,7 +35,6 @@
</div>
<% } %>
</div>
</div>
<div class="ui bottom attached tab segment" data-tab="preview">
<div class="ui header" id="pv-title"></div>
<div id="pv-content" class="font-content"></div>
@ -40,17 +48,29 @@
</div>
<script type="text/javascript">
var editors = {
content: { defaultValue: <%- serializejs(article.content) %> },
};
window.onEditorLoaded(function () {
for (var name in editors) {
var editor = editors[name];
var editorElement = document.getElementById(name);
var input = document.getElementsByName(name)[0];
editor.editor = window.createMarkdownEditor(editorElement, editor.defaultValue, input);
}
});
$(function () {
function render(output, input) {
$.post('/api/markdown', { s: input.val() }, function (s) {
// console.log(s);
$.post('/api/markdown', { s: editors[input].editor.getValue() }, function (s) {
output.html(s);
});
}
$("#preview_tab").click(function () {
$("#pv-title").text($("#title").val());
$("#pv-content").text('Loading...');
render($("#pv-content"), $("#content"));
render($("#pv-content"), "content");
});
$('.tabular.menu .item').tab();
})

33
views/contest_edit.ejs

@ -1,15 +1,22 @@
<% this.title = contest.id ? '编辑比赛' : '新建比赛' %>
<% include header %>
<% include monaco-editor %>
<div class="padding">
<form class="ui form" action="<%= syzoj.utils.makeUrl(['contest', contest.id, 'edit']) %>" method="post">
<form action="<%= syzoj.utils.makeUrl(['contest', contest.id, 'edit']) %>" method="post">
<div class="ui form">
<div class="field">
<label>比赛名称</label>
<input type="text" name="title" value="<%= contest.title %>">
</div>
<div class="field">
<label>比赛描述</label>
<input type="text" name="subtitle" class="markdown-edit" value="<%= contest.subtitle %>">
</div>
</div>
<div id="subtitle" class="editor editor-with-border" style="height: 100px; width: 100%; margin-bottom: 1em; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="subtitle">
<div class="ui form">
<div class="field">
<label>试题列表</label>
<select class="ui fluid search dropdown" multiple="" id="search_problems" name="problems">
@ -53,8 +60,13 @@
</div>
<div class="field">
<label>比赛公告</label>
<textarea class="" rows="5" id="doc-ta-1" name="information" class="markdown-edit"><%= contest.information %></textarea>
</div>
</div>
<div id="information" class="editor editor-with-border" style="height: 100px; width: 100%; margin-bottom: 1em; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="information">
<div class="ui form">
<div class="field">
<label>开始时间</label>
<input type="text" name="start_time" value="<%= syzoj.utils.formatDate(contest.start_time || syzoj.utils.getCurrentDate()) %>">
@ -78,8 +90,23 @@
</div>
</div>
<div style="text-align: center; "><button type="submit" class="ui labeled icon blue button"><i class="ui edit icon"></i>提交</button></div>
</div>
</form>
<script>
var editors = {
subtitle: { defaultValue: <%- serializejs(contest.subtitle) %> },
information: { defaultValue: <%- serializejs(contest.information) %> }
};
window.onEditorLoaded(function () {
for (var name in editors) {
var editor = editors[name];
var editorElement = document.getElementById(name);
var input = document.getElementsByName(name)[0];
editor.editor = window.createMarkdownEditor(editorElement, editor.defaultValue, input);
}
});
$(function () {
$('#search_admins')
.dropdown({

1
views/header.ejs

@ -11,7 +11,6 @@
<link href="<%- selfLib('math.css') %>" rel="stylesheet">
<link href="<%- selfLib('style.css') %>?20181212" rel="stylesheet">
<link href="<%- lib('morris.js/0.5.1/morris.css') %>" rel="stylesheet">
<link href="<%- lib('monaco-editor/0.16.2/min/vs/editor/editor.main.css') %>" rel="stylesheet">
<% if (useLocalLibs || !syzoj.config.google_fonts || !syzoj.config.google_fonts.url || !['like-google-fonts', 'like-local'].includes(syzoj.config.google_fonts.type)) { %>
<link href="<%- syzoj.utils.makeUrl(['google-fonts', 'fira-mono.css']) %>" rel="stylesheet">
<link href="<%- syzoj.utils.makeUrl(['google-fonts', 'lato.css']) %>" rel="stylesheet">

15
views/monaco-editor.ejs

@ -1,8 +1,23 @@
<script>
window.pathLib = "<%- lib('') %>";
window.pathSelfLib = "<%- selfLib('') %>";
var originalConsoleWarn = console.warn;
console.warn = function (message) {
if (message.startsWith('Duplicate definition of module')) return;
originalConsoleWarn.apply(console, arguments);
};
</script>
<!-- Load monaco-editor -->
<script src="<%- lib('monaco-editor/0.16.2/min/vs/loader.js') %>"></script>
<script src="<%- selfLib('monaco-editor.js') %>"></script>
<link rel="stylesheet" href="<%- selfLib('monaco-editor.css') %>">
<%
this.showLoadingEditor = () => {
%>
<div class="ui active medium text loader">编辑器加载中 …</div>
<%
};
%>

22
views/problem.ejs

@ -241,15 +241,17 @@ if (contest) {
<div class="four wide column" style="margin-right: -25px; ">
<div class="ui attached vertical fluid pointing menu" id="testcase-menu" style="height: 370px; overflow-y: scroll; overflow-x: hidden; ">
<% for (let i = 0; i < cases.length; i++) { %>
<a class="item<%= i === 0 ? ' active' : '' %>" data-value="<%= i %>">
<a style="border-radius: 0; " class="item<%= i === 0 ? ' active' : '' %>" data-value="<%= i %>">
<%= cases[i] %>
</a>
<% } %>
</div>
</div>
<div class="twelve wide stretched column" style="position: relative; padding-left: 0; margin-left: calc(-1rem - 1px); width: calc(75% + 1rem + 12px) !important; ">
<div class="twelve wide stretched column" style="position: relative; padding-left: 0; margin-left: calc(-1rem - 1px); width: calc(75% + 1rem + 13px) !important; ">
<% for (let i = 0; i < cases.length; i++) { %>
<div id="editor-<%= i %>" class="editor" style="position: absolute; width: 100%; height: calc(100% - 28px); border: 1px solid #D4D4D5; <%= i === 0 ? '' : 'visibility: hidden; ' %>"></div>
<div id="editor-<%= i %>" class="editor" style="position: absolute; width: 100%; height: calc(100% - 28px); border: 1px solid #D4D4D5; overflow: hidden; <%= i === 0 ? '' : 'visibility: hidden; ' %>">
<%- this.showLoadingEditor(); %>
</div>
<% } %>
</div>
<% } %>
@ -258,7 +260,7 @@ if (contest) {
window.onEditorLoaded(function () {
window.editors = [];
for (var i = 0; i < cases.length; i++) {
var editor = window.createEditor(document.getElementById("editor-" + i));
var editor = window.createCodeEditor(document.getElementById("editor-" + i));
editors[i] = editor;
}
@ -305,15 +307,17 @@ if (contest) {
} else if (lastLanguage) language = lastLanguage;
%>
<% for (lang of syzoj.config.enabled_languages) { %>
<a class="item<%= lang === language ? ' active' : '' %>" data-value="<%= lang %>" data-mode="<%= syzoj.languages[lang].editor %>">
<a style="border-radius: 0; " class="item<%= lang === language ? ' active' : '' %>" data-value="<%= lang %>" data-mode="<%= syzoj.languages[lang].editor %>">
<%= syzoj.languages[lang].show %>
<div class="ui right floated" style="opacity: 0.4; margin-top: 8px; font-size: 0.7em; "><%= syzoj.languages[lang].version %></div>
</a>
<% } %>
</div>
</div>
<div class="twelve wide stretched column" style="padding-left: 0; margin-left: calc(-1rem - 1px); width: calc(75% + 1rem + 1px + 25px) !important; ">
<div id="editor" style="border: 1px solid #D4D4D5; " class="editor"></div>
<div class="twelve wide stretched column" style="position: relative; padding-left: 0; margin-left: calc(-1rem - 1px); width: calc(75% + 1rem + 13px) !important; ">
<div id="editor" style="position: absolute; width: 100%; height: calc(100% - 28px); border: 1px solid #D4D4D5; overflow: hidden; " class="editor">
<%- this.showLoadingEditor(); %>
</div>
</div>
<div class="ui form" style="width: 100%; ">
<div class="inline fields" style="width: 100%; ">
@ -341,7 +345,7 @@ if (contest) {
var editorElement = document.getElementById('editor');
var content = '';
<% if (state) { %>content = <%- serializejs(state.code) %>;<% } %>
editor = window.createEditor(editorElement, 'ace_' + $('#languages-menu .item.active').data('mode'), content);
editor = window.createCodeEditor(editorElement, $('#languages-menu .item.active').data('mode'), content);
window.editor = editor;
});
@ -366,7 +370,7 @@ if (contest) {
.not($(this))
.removeClass('active')
;
monaco.editor.setModelLanguage(editor.getModel(), 'ace_' + $(this).data('mode'));
monaco.editor.setModelLanguage(editor.getModel(), $(this).data('mode'));
});
});
</script>

82
views/problem_edit.ejs

@ -5,14 +5,15 @@
text-align: center;
}
</style>
<% include monaco-editor %>
<div class="padding">
<form class="ui form" method="post" action="<%= syzoj.utils.makeUrl(['problem', req.params.id, 'edit']) %>">
<form method="post" action="<%= syzoj.utils.makeUrl(['problem', req.params.id, 'edit']) %>">
<div class="ui top attached tabular menu">
<a class="item active" data-tab="edit">编辑</a>
<a class="item" data-tab="preview" id="preview_tab">预览</a>
</div>
<div class="ui bottom attached tab segment active" data-tab="edit">
<div class="field">
<div class="ui form"><div class="field">
<% if (problem.allowedManage) { %>
<label for="id">
<% if (problem.new) { %>
@ -27,16 +28,47 @@
<% } %>
<label for="title">题目名称</label>
<input class="font-content" type="text" id="title" name="title" value="<%= problem.title %>">
<label style="margin-top: 15px; " for="description">题目描述</label>
<textarea class="markdown-edit" rows="15" id="description" name="description"><%= problem.description %></textarea>
</div></div>
<div class="editor editor-with-border" id="description" style="width: 100%; height: 300px; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="description">
<div class="ui form"><div class="field">
<label style="margin-top: 15px; " for="input_format">输入格式</label>
<textarea class="markdown-edit" rows="10" id="input" name="input_format"><%= problem.input_format %></textarea>
</div></div>
<div class="editor editor-with-border" id="input_format" style="width: 100%; height: 200px; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="input_format">
<div class="ui form"><div class="field">
<label style="margin-top: 15px; " for="output_format">输出格式</label>
<textarea class="markdown-edit" rows="10" id="output" name="output_format"><%= problem.output_format %></textarea>
<label style="margin-top: 15px; ">样例</label>
<textarea class="markdown-edit" rows="15" id="example" name="example"><%= problem.example %></textarea>
<label style="margin-top: 15px; ">数据范围与提示</label>
<textarea class="markdown-edit" rows="10" id="hint" name="limit_and_hint"><%= problem.limit_and_hint %></textarea>
</div></div>
<div class="editor editor-with-border" id="output_format" style="width: 100%; height: 200px; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="output_format">
<div class="ui form"><div class="field">
<label style="margin-top: 15px; " for="example">样例</label>
</div></div>
<div class="editor editor-with-border" id="example" style="width: 100%; height: 300px; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="example">
<div class="ui form"><div class="field">
<label style="margin-top: 15px; " for="limit_and_hint">数据范围与提示</label>
</div></div>
<div class="editor editor-with-border" id="limit_and_hint" style="width: 100%; height: 200px; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="limit_and_hint">
<div class="ui form"><div class="field">
<label style="margin-top: 15px; ">标签</label>
<select class="ui fluid search dropdown" multiple="" id="search_tags" name="tags">
<% for (let tag of problem.tags) { %>
@ -48,7 +80,7 @@
<label><strong>匿名上传</strong></label>
<p style="margin-top: 5px; ">选择后,上传者的用户名将不在题目页面中显示。</p>
</div>
</div>
</div></div>
</div>
<div class="ui bottom attached tab segment" data-tab="preview" id="preview">
<h1 class="ui header problem_header" id="pv-title"></h1>
@ -73,21 +105,37 @@
</form>
</div>
<script type="text/javascript">
var editors = {
description: { defaultValue: <%- serializejs(problem.description) %> },
input_format: { defaultValue: <%- serializejs(problem.input_format) %> },
output_format: { defaultValue: <%- serializejs(problem.output_format) %> },
example: { defaultValue: <%- serializejs(problem.example) %> },
limit_and_hint: { defaultValue: <%- serializejs(problem.limit_and_hint) %> }
};
window.onEditorLoaded(function () {
for (var name in editors) {
var editor = editors[name];
var editorElement = document.getElementById(name);
var input = document.getElementsByName(name)[0];
editor.editor = window.createMarkdownEditor(editorElement, editor.defaultValue, input);
}
});
$(function () {
function render(output, input) {
$.post('/api/markdown', { s: input.val() }, function (s) {
// console.log(s);
$.post('/api/markdown', { s: editors[input].editor.getValue() }, function (s) {
output.html(s);
});
}
$("#preview_tab").click(function () {
$("#pv-title").text($("#title").val());
$("#pv-description, #pv-input, #pv-output, #pv-example, #pv-hint").text('Loading...');
render($("#pv-description"), $("#description"));
render($("#pv-input"), $("#input"));
render($("#pv-output"), $("#output"));
render($("#pv-example"), $("#example"));
render($("#pv-hint"), $("#hint"));
render($("#pv-description"), "description");
render($("#pv-input"), "input_format");
render($("#pv-output"), "output_format");
render($("#pv-example"), "example");
render($("#pv-hint"), "limit_and_hint");
});
$('.tabular.menu .item').tab();
});

27
views/user_edit.ejs

@ -1,5 +1,6 @@
<% this.title = '修改资料'; %>
<% include header %>
<% include monaco-editor %>
<div class="padding">
<div class="ui <% if (error_info === '') { %>success<% } else { %>error<% } %> message" id="error" <% if (error_info === null) { %>hidden<% } %>>
<% if (error_info !== null) {
@ -8,7 +9,8 @@
<p id="error_info"><%= error_info %></p>
<% } %>
</div>
<form id="form" class="ui form" action="<%= syzoj.utils.makeUrl(['user', edited_user.id, 'edit']) %>" method="post" onsubmit="return check()">
<form id="form" action="<%= syzoj.utils.makeUrl(['user', edited_user.id, 'edit']) %>" method="post" onsubmit="return check()">
<div class="ui form">
<div class="field">
<label for="username">用户名</label>
<input type="text" id="username" name="username" value="<%= edited_user.username %>"<% if (!user.allowedManage) { %> readonly<% } %>>
@ -41,8 +43,15 @@
</div>
<div class="field">
<label for="information">个性签名</label>
<textarea class="font-content" rows="5" id="information" name="information"><%= edited_user.information %></textarea>
</div>
</div>
<div class="editor editor-with-border" id="information" style="width: 100%; height: 100px; margin-bottom: 1em; ">
<%- this.showLoadingEditor(); %>
</div>
<input type="hidden" name="information">
<div class="ui form">
<div class="field">
<label class="ui header">密码</label>
<input type="password" placeholder="原密码(留空则不修改)" name="old_password" id="old_password">
@ -80,12 +89,26 @@
<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>
</div>
</form>
</div>
</div>
<script src="<%- lib('blueimp-md5/2.10.0/js/md5.min.js') %>"></script>
<script type="text/javascript">
var editors = {
information: { defaultValue: <%- serializejs(edited_user.information) %> },
};
window.onEditorLoaded(function () {
for (var name in editors) {
var editor = editors[name];
var editorElement = document.getElementById(name);
var input = document.getElementsByName(name)[0];
editor.editor = window.createMarkdownEditor(editorElement, editor.defaultValue, input);
}
});
function make_md5(tag) {
if (tag.val()) {
tag.val(md5(tag.val() + "syzoj2_xxx"));

Loading…
Cancel
Save