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. 662
      static/self/monaco-editor.js
  4. 8
      views/admin_raw.ejs
  5. 28
      views/article.ejs
  6. 54
      views/article_edit.ejs
  7. 41
      views/contest_edit.ejs
  8. 1
      views/header.ejs
  9. 15
      views/monaco-editor.ejs
  10. 22
      views/problem.ejs
  11. 88
      views/problem_edit.ejs
  12. 31
      views/user_edit.ejs

18
language-config.json

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

41
static/self/monaco-editor.css

@ -1,6 +1,26 @@
.editor { .editor {
background: #fff; 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 { .monaco-editor .inputarea {
@ -29,9 +49,22 @@
} }
.monaco-editor .line-numbers { .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 { .editor.editor-with-border .monaco-editor .monaco-scrollable-element .scrollbar.vertical .slider {
background-color: #efefef !important; border-radius: 5px;
background: rgba(0,0,0,.25);
-webkit-transition: color .2s ease;
transition: color .2s ease;
} }

662
static/self/monaco-editor.js

@ -33,79 +33,110 @@ require(['vs/editor/editor.main'], function () {
PascalDefinition, PascalDefinition,
PythonDefinition, PythonDefinition,
RubyDefinition) { RubyDefinition) {
monaco.languages.register({ id: 'ace_c_cpp' }); var overrideLangauges = [
MonacoAceTokenizer.registerRulesForLanguage('ace_c_cpp', new c_cppDefinition.default); 'cpp',
monaco.languages.setLanguageConfiguration('ace_c_cpp', { 'c',
comments: { 'csharp',
lineComment: '//', 'haskell',
blockComment: ['/*', '*/'], 'java',
}, 'javascript',
brackets: [ 'pascal',
['{', '}'], 'python',
['[', ']'], 'ruby',
['(', ')'] 'markdown'
], ];
autoClosingPairs: [
{ open: '[', close: ']' }, monaco.languages.getLanguages().forEach(function (lang) {
{ open: '{', close: '}' }, if (overrideLangauges.includes(lang.id)) {
{ open: '(', close: ')' }, lang.loader = function () {
{ open: '\'', close: '\'', notIn: ['string', 'comment'] }, return { then: function () {} };
{ open: '"', close: '"', notIn: ['string'] }, };
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: '\'', close: '\'' },
],
folding: {
markers: {
start: new RegExp("^\\s*#pragma\\s+region\\b"),
end: new RegExp("^\\s*#pragma\\s+endregion\\b")
}
} }
}); });
monaco.languages.register({ id: 'ace_csharp' }); var cppAliases = ['c', 'cpp', 'c++', 'cxx', 'cc'];
MonacoAceTokenizer.registerRulesForLanguage('ace_csharp', new CSharpDefinition.default); for (var i in cppAliases) {
monaco.languages.setLanguageConfiguration('ace_csharp', { var alias = cppAliases[i];
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: { monaco.languages.register({ id: alias });
MonacoAceTokenizer.registerRulesForLanguage(alias, new c_cppDefinition.default);
monaco.languages.setLanguageConfiguration(alias, {
comments: {
lineComment: '//', lineComment: '//',
blockComment: ['/*', '*/'], blockComment: ['/*', '*/'],
}, },
brackets: [ brackets: [
['{', '}'], ['{', '}'],
['[', ']'], ['[', ']'],
['(', ')'], ['(', ')']
], ],
autoClosingPairs: [ autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' }, { open: '[', close: ']' },
{ open: '{', close: '}' },
{ open: '(', close: ')' }, { open: '(', close: ')' },
{ open: '\'', close: '\'', notIn: ['string', 'comment'] }, { open: '\'', close: '\'', notIn: ['string', 'comment'] },
{ open: '"', close: '"', notIn: ['string', 'comment'] }, { open: '"', close: '"', notIn: ['string'] },
], ],
surroundingPairs: [ surroundingPairs: [
{ open: '{', close: '}' }, { open: '{', close: '}' },
{ open: '[', close: ']' }, { open: '[', close: ']' },
{ open: '(', close: ')' }, { open: '(', close: ')' },
{ open: '<', close: '>' },
{ open: '\'', close: '\'' },
{ open: '"', close: '"' }, { open: '"', close: '"' },
], { open: '\'', close: '\'' },
folding: { ],
folding: {
markers: { markers: {
start: new RegExp("^\\s*#region\\b"), start: new RegExp("^\\s*#pragma\\s+region\\b"),
end: new RegExp("^\\s*#endregion\\b") end: new RegExp("^\\s*#pragma\\s+endregion\\b")
} }
} }
}); });
}
var csharpAliases = ['csharp', 'cs', 'c#'];
for (var i in csharpAliases) {
var alias = csharpAliases[i];
monaco.languages.register({ id: 'ace_haskell' }); monaco.languages.register({ id: alias });
MonacoAceTokenizer.registerRulesForLanguage('ace_haskell', new HaskellDefinition.default); MonacoAceTokenizer.registerRulesForLanguage(alias, new CSharpDefinition.default);
monaco.languages.setLanguageConfiguration('ace_haskell', { monaco.languages.setLanguageConfiguration(alias, {
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
lineComment: '//',
blockComment: ['/*', '*/'],
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
{ open: '"', close: '"', notIn: ['string', 'comment'] },
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '<', close: '>' },
{ open: '\'', close: '\'' },
{ open: '"', close: '"' },
],
folding: {
markers: {
start: new RegExp("^\\s*#region\\b"),
end: new RegExp("^\\s*#endregion\\b")
}
}
});
}
monaco.languages.register({ id: 'haskell' });
MonacoAceTokenizer.registerRulesForLanguage('haskell', new HaskellDefinition.default);
monaco.languages.setLanguageConfiguration('haskell', {
comments: { comments: {
lineComment: '--', lineComment: '--',
blockComment: ['{-', '-}'] blockComment: ['{-', '-}']
@ -136,9 +167,9 @@ require(['vs/editor/editor.main'], function () {
} }
}); });
monaco.languages.register({ id: 'ace_java' }); monaco.languages.register({ id: 'java' });
MonacoAceTokenizer.registerRulesForLanguage('ace_java', new JavaDefinition.default); MonacoAceTokenizer.registerRulesForLanguage('java', new JavaDefinition.default);
monaco.languages.setLanguageConfiguration('ace_java', { monaco.languages.setLanguageConfiguration('java', {
// the default separators except `@$` // the default separators except `@$`
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: { comments: {
@ -173,140 +204,154 @@ require(['vs/editor/editor.main'], function () {
} }
}); });
monaco.languages.register({ id: 'ace_javascript' }); var javascriptAliases = ['javascript', 'js'];
MonacoAceTokenizer.registerRulesForLanguage('ace_javascript', new JavaScriptDefinition.default); for (var i in javascriptAliases) {
monaco.languages.setLanguageConfiguration('ace_javascript', { var alias = javascriptAliases[i];
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, monaco.languages.register({ id: alias });
comments: { MonacoAceTokenizer.registerRulesForLanguage(alias, new JavaScriptDefinition.default);
lineComment: '//', monaco.languages.setLanguageConfiguration(alias, {
blockComment: ['/*', '*/'] wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
}, comments: {
brackets: [ lineComment: '//',
['{', '}'], blockComment: ['/*', '*/']
['[', ']'], },
['(', ')'] brackets: [
], ['{', '}'],
onEnterRules: [ ['[', ']'],
{ ['(', ')']
// e.g. /** | */ ],
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, onEnterRules: [
afterText: /^\s*\*\/$/, {
action: { indentAction: monaco.languages.IndentAction.IndentOutdent, appendText: ' * ' } // e.g. /** | */
}, beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
{ afterText: /^\s*\*\/$/,
// e.g. /** ...| action: { indentAction: monaco.languages.IndentAction.IndentOutdent, appendText: ' * ' }
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, },
action: { indentAction: monaco.languages.IndentAction.None, appendText: ' * ' } {
}, // e.g. /** ...|
{ beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
// e.g. * ...| action: { indentAction: monaco.languages.IndentAction.None, appendText: ' * ' }
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/, },
action: { indentAction: monaco.languages.IndentAction.None, appendText: '* ' } {
}, // e.g. * ...|
{ beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
// e.g. */| action: { indentAction: monaco.languages.IndentAction.None, appendText: '* ' }
beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/, },
action: { indentAction: monaco.languages.IndentAction.None, removeText: 1 } {
} // e.g. */|
], beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
autoClosingPairs: [ action: { indentAction: monaco.languages.IndentAction.None, removeText: 1 }
{ open: '{', close: '}' }, }
{ open: '[', close: ']' }, ],
{ open: '(', close: ')' }, autoClosingPairs: [
{ open: '"', close: '"', notIn: ['string'] }, { open: '{', close: '}' },
{ open: '\'', close: '\'', notIn: ['string', 'comment'] }, { open: '[', close: ']' },
{ open: '`', close: '`', notIn: ['string', 'comment'] }, { open: '(', close: ')' },
{ open: "/**", close: " */", notIn: ["string"] } { open: '"', close: '"', notIn: ['string'] },
], { open: '\'', close: '\'', notIn: ['string', 'comment'] },
folding: { { open: '`', close: '`', notIn: ['string', 'comment'] },
markers: { { open: "/**", close: " */", notIn: ["string"] }
start: new RegExp("^\\s*//\\s*#?region\\b"), ],
end: new RegExp("^\\s*//\\s*#?endregion\\b") folding: {
} markers: {
} start: new RegExp("^\\s*//\\s*#?region\\b"),
}); end: new RegExp("^\\s*//\\s*#?endregion\\b")
}
monaco.languages.register({ id: 'ace_pascal' });
MonacoAceTokenizer.registerRulesForLanguage('ace_pascal', new PascalDefinition.default);
monaco.languages.setLanguageConfiguration('ace_pascal', {
// the default separators except `@$`
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
lineComment: '//',
blockComment: ['{', '}'],
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
['<', '>'],
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '<', close: '>' },
{ open: '\'', close: '\'' },
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '<', close: '>' },
{ open: '\'', close: '\'' },
],
folding: {
markers: {
start: new RegExp("^\\s*\\{\\$REGION(\\s\\'.*\\')?\\}"),
end: new RegExp("^\\s*\\{\\$ENDREGION\\}")
} }
} });
}); }
monaco.languages.register({ id: 'ace_python' }); var pascalAliases = ['pascal', 'pas'];
MonacoAceTokenizer.registerRulesForLanguage('ace_python', new PythonDefinition.default); for (var i in pascalAliases) {
monaco.languages.setLanguageConfiguration('ace_python', { var alias = pascalAliases[i];
comments: {
lineComment: '#', monaco.languages.register({ id: alias });
blockComment: ['\'\'\'', '\'\'\''], MonacoAceTokenizer.registerRulesForLanguage(alias, new PascalDefinition.default);
}, monaco.languages.setLanguageConfiguration(alias, {
brackets: [ // the default separators except `@$`
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
comments: {
lineComment: '//',
blockComment: ['{', '}'],
},
brackets: [
['{', '}'], ['{', '}'],
['[', ']'], ['[', ']'],
['(', ')'] ['(', ')'],
], ['<', '>'],
autoClosingPairs: [ ],
autoClosingPairs: [
{ open: '{', close: '}' }, { open: '{', close: '}' },
{ open: '[', close: ']' }, { open: '[', close: ']' },
{ open: '(', close: ')' }, { open: '(', close: ')' },
{ open: '"', close: '"', notIn: ['string'] }, { open: '<', close: '>' },
{ open: '\'', close: '\'', notIn: ['string', 'comment'] }, { open: '\'', close: '\'' },
], ],
surroundingPairs: [ surroundingPairs: [
{ open: '{', close: '}' }, { open: '{', close: '}' },
{ open: '[', close: ']' }, { open: '[', close: ']' },
{ open: '(', close: ')' }, { open: '(', close: ')' },
{ open: '"', close: '"' }, { open: '<', close: '>' },
{ open: '\'', close: '\'' }, { open: '\'', close: '\'' },
], ],
onEnterRules: [ folding: {
{
beforeText: new RegExp("^\\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\\s*$"),
action: { indentAction: monaco.languages.IndentAction.Indent }
}
],
folding: {
offSide: true,
markers: { markers: {
start: new RegExp("^\\s*#region\\b"), start: new RegExp("^\\s*\\{\\$REGION(\\s\\'.*\\')?\\}"),
end: new RegExp("^\\s*#endregion\\b") end: new RegExp("^\\s*\\{\\$ENDREGION\\}")
} }
} }
}); });
}
var pythonAliases = ['python', 'python2', 'python3', 'py', 'py2', 'py3'];
for (var i in pythonAliases) {
var alias = pythonAliases[i];
monaco.languages.register({ id: alias });
MonacoAceTokenizer.registerRulesForLanguage(alias, new PythonDefinition.default);
monaco.languages.setLanguageConfiguration(alias, {
comments: {
lineComment: '#',
blockComment: ['\'\'\'', '\'\'\''],
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"', notIn: ['string'] },
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: '\'', close: '\'' },
],
onEnterRules: [
{
beforeText: new RegExp("^\\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async).*?:\\s*$"),
action: { indentAction: monaco.languages.IndentAction.Indent }
}
],
folding: {
offSide: true,
markers: {
start: new RegExp("^\\s*#region\\b"),
end: new RegExp("^\\s*#endregion\\b")
}
}
});
}
monaco.languages.register({ id: 'ace_ruby' }); monaco.languages.register({ id: 'ruby' });
MonacoAceTokenizer.registerRulesForLanguage('ace_ruby', new RubyDefinition.default); MonacoAceTokenizer.registerRulesForLanguage('ruby', new RubyDefinition.default);
monaco.languages.setLanguageConfiguration('ace_ruby', { monaco.languages.setLanguageConfiguration('ruby', {
comments: { comments: {
lineComment: '#', lineComment: '#',
blockComment: ['=begin', '=end'], 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 () { $.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, { return monaco.editor.create(editorElement, {
value: content, value: content,
language: langauge, language: langauge,
@ -347,10 +595,60 @@ require(['vs/editor/editor.main'], function () {
theme: 'tomorrow', theme: 'tomorrow',
lineHeight: 22, lineHeight: 22,
fontSize: 14, 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,
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, lineNumbersMinChars: 4,
glyphMargin: false 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; window.editorLoaded = true;

8
views/admin_raw.ejs

@ -2,10 +2,12 @@
<% include admin_header %> <% include admin_header %>
<form method="post"> <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"> <input type="hidden" name="data">
<% include monaco-editor %>
<script type="text/javascript"> <script type="text/javascript">
var originalData = <%- serializejs(data) %>; var originalData = <%- serializejs(data) %>;
var input = $('[name="data"]'); var input = $('[name="data"]');
@ -13,7 +15,7 @@ input.val(originalData);
window.onEditorLoaded(function () { window.onEditorLoaded(function () {
var editor; 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 () { $('form').on('submit', function () {
input.val(editor.getValue()); input.val(editor.getValue());
}); });

28
views/article.ejs

@ -94,17 +94,33 @@
<% include page %> <% include page %>
</div> </div>
<% } %> <% } %>
<% if (article.allowedComment) { %> <% if (article.allowedComment) { %>
<form class="ui reply form" method="post" action="<%= syzoj.utils.makeUrl(['article', article.id, 'comment']) %>"> <% include monaco-editor %>
<div class="field"> <form class="ui reply form" method="post" action="<%= syzoj.utils.makeUrl(['article', article.id, 'comment']) %>">
<textarea name="comment"></textarea> <div id="comment" class="editor editor-with-border" style="height: 200px; width: 100%; margin-bottom: 1em; ">
<%- this.showLoadingEditor(); %>
</div> </div>
<input type="hidden" name="comment">
<div style="text-align: center; "> <div style="text-align: center; ">
<button type="submit" class="ui labeled submit icon button"> <button type="submit" class="ui labeled submit icon button">
<i class="icon edit"></i> 回复 <i class="icon edit"></i> 回复
</button> </button>
</div> </div>
</form> </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> </div>
<% include footer %> <% include footer %>

54
views/article_edit.ejs

@ -1,31 +1,39 @@
<% this.title = article.id ? '编辑帖子' : '发表帖子'; %> <% this.title = article.id ? '编辑帖子' : '发表帖子'; %>
<% include header %> <% include header %>
<% include monaco-editor %>
<style type="text/css"> <style type="text/css">
.am-panel-title{ .am-panel-title{
font-size: 1.6em; font-size: 1.6em;
} }
</style> </style>
<div class="padding"> <div class="padding">
<form class="ui form" method="post"> <form class="" method="post">
<div class="ui top attached tabular menu"> <div class="ui top attached tabular menu">
<a class="item active" data-tab="edit">编辑</a> <a class="item active" data-tab="edit">编辑</a>
<a class="item" data-tab="preview" id="preview_tab">预览</a> <a class="item" data-tab="preview" id="preview_tab">预览</a>
</div> </div>
<div class="ui bottom attached tab segment active" data-tab="edit"> <div class="ui bottom attached tab segment active" data-tab="edit">
<div class="field"> <div class="ui form">
<label for="title">标题</label> <div class="field">
<input type="text" id="title" name="title" value="<%= article.title %>"> <label for="title">标题</label>
<div style="margin-top: 15px; "></div> <input type="text" id="title" name="title" value="<%= article.title %>">
<label for="content">内容</label> </div>
<textarea rows="15" id="content" name="content" class="markdown-edit"><%= article.content %></textarea> <div class="field">
<% if (user && user.is_admin) { %> <label for="content">内容</label>
<div class="ui <% if (article.is_notice) { %>checked <% } %>checkbox" style="margin-top: 15px; "> </div>
<input <% if (article.is_notice) { %>checked=""<% } %> name="is_notice" type="checkbox">
<label><strong>公告</strong></label>
<p style="margin-top: 5px; ">选择后将显示在首页公告栏。</p>
</div>
<% } %>
</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">
<label><strong>公告</strong></label>
<p style="margin-top: 5px; ">选择后将显示在首页公告栏。</p>
</div>
<% } %>
</div> </div>
<div class="ui bottom attached tab segment" data-tab="preview"> <div class="ui bottom attached tab segment" data-tab="preview">
<div class="ui header" id="pv-title"></div> <div class="ui header" id="pv-title"></div>
@ -40,17 +48,29 @@
</div> </div>
<script type="text/javascript"> <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 () {
function render(output, input) { function render(output, input) {
$.post('/api/markdown', { s: input.val() }, function (s) { $.post('/api/markdown', { s: editors[input].editor.getValue() }, function (s) {
// console.log(s);
output.html(s); output.html(s);
}); });
} }
$("#preview_tab").click(function () { $("#preview_tab").click(function () {
$("#pv-title").text($("#title").val()); $("#pv-title").text($("#title").val());
$("#pv-content").text('Loading...'); $("#pv-content").text('Loading...');
render($("#pv-content"), $("#content")); render($("#pv-content"), "content");
}); });
$('.tabular.menu .item').tab(); $('.tabular.menu .item').tab();
}) })

41
views/contest_edit.ejs

@ -1,15 +1,22 @@
<% this.title = contest.id ? '编辑比赛' : '新建比赛' %> <% this.title = contest.id ? '编辑比赛' : '新建比赛' %>
<% include header %> <% include header %>
<% include monaco-editor %>
<div class="padding"> <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="field"> <div class="ui form">
<div class="field">
<label>比赛名称</label> <label>比赛名称</label>
<input type="text" name="title" value="<%= contest.title %>"> <input type="text" name="title" value="<%= contest.title %>">
</div> </div>
<div class="field"> <div class="field">
<label>比赛描述</label> <label>比赛描述</label>
<input type="text" name="subtitle" class="markdown-edit" value="<%= contest.subtitle %>"> </div>
</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"> <div class="field">
<label>试题列表</label> <label>试题列表</label>
<select class="ui fluid search dropdown" multiple="" id="search_problems" name="problems"> <select class="ui fluid search dropdown" multiple="" id="search_problems" name="problems">
@ -53,8 +60,13 @@
</div> </div>
<div class="field"> <div class="field">
<label>比赛公告</label> <label>比赛公告</label>
<textarea class="" rows="5" id="doc-ta-1" name="information" class="markdown-edit"><%= contest.information %></textarea>
</div> </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"> <div class="field">
<label>开始时间</label> <label>开始时间</label>
<input type="text" name="start_time" value="<%= syzoj.utils.formatDate(contest.start_time || syzoj.utils.getCurrentDate()) %>"> <input type="text" name="start_time" value="<%= syzoj.utils.formatDate(contest.start_time || syzoj.utils.getCurrentDate()) %>">
@ -78,8 +90,23 @@
</div> </div>
</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 style="text-align: center; "><button type="submit" class="ui labeled icon blue button"><i class="ui edit icon"></i>提交</button></div>
</div>
</form> </form>
<script> <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 () { $(function () {
$('#search_admins') $('#search_admins')
.dropdown({ .dropdown({

1
views/header.ejs

@ -11,7 +11,6 @@
<link href="<%- selfLib('math.css') %>" rel="stylesheet"> <link href="<%- selfLib('math.css') %>" rel="stylesheet">
<link href="<%- selfLib('style.css') %>?20181212" 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('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)) { %> <% 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', 'fira-mono.css']) %>" rel="stylesheet">
<link href="<%- syzoj.utils.makeUrl(['google-fonts', 'lato.css']) %>" rel="stylesheet"> <link href="<%- syzoj.utils.makeUrl(['google-fonts', 'lato.css']) %>" rel="stylesheet">

15
views/monaco-editor.ejs

@ -1,8 +1,23 @@
<script> <script>
window.pathLib = "<%- lib('') %>"; window.pathLib = "<%- lib('') %>";
window.pathSelfLib = "<%- selfLib('') %>"; window.pathSelfLib = "<%- selfLib('') %>";
var originalConsoleWarn = console.warn;
console.warn = function (message) {
if (message.startsWith('Duplicate definition of module')) return;
originalConsoleWarn.apply(console, arguments);
};
</script> </script>
<!-- Load monaco-editor -->
<script src="<%- lib('monaco-editor/0.16.2/min/vs/loader.js') %>"></script> <script src="<%- lib('monaco-editor/0.16.2/min/vs/loader.js') %>"></script>
<script src="<%- selfLib('monaco-editor.js') %>"></script> <script src="<%- selfLib('monaco-editor.js') %>"></script>
<link rel="stylesheet" href="<%- selfLib('monaco-editor.css') %>"> <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="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; "> <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++) { %> <% 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] %> <%= cases[i] %>
</a> </a>
<% } %> <% } %>
</div> </div>
</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++) { %> <% 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> </div>
<% } %> <% } %>
@ -258,7 +260,7 @@ if (contest) {
window.onEditorLoaded(function () { window.onEditorLoaded(function () {
window.editors = []; window.editors = [];
for (var i = 0; i < cases.length; i++) { 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; editors[i] = editor;
} }
@ -305,15 +307,17 @@ if (contest) {
} else if (lastLanguage) language = lastLanguage; } else if (lastLanguage) language = lastLanguage;
%> %>
<% for (lang of syzoj.config.enabled_languages) { %> <% 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 %> <%= 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> <div class="ui right floated" style="opacity: 0.4; margin-top: 8px; font-size: 0.7em; "><%= syzoj.languages[lang].version %></div>
</a> </a>
<% } %> <% } %>
</div> </div>
</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 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="border: 1px solid #D4D4D5; " class="editor"></div> <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>
<div class="ui form" style="width: 100%; "> <div class="ui form" style="width: 100%; ">
<div class="inline fields" style="width: 100%; "> <div class="inline fields" style="width: 100%; ">
@ -341,7 +345,7 @@ if (contest) {
var editorElement = document.getElementById('editor'); var editorElement = document.getElementById('editor');
var content = ''; var content = '';
<% if (state) { %>content = <%- serializejs(state.code) %>;<% } %> <% 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; window.editor = editor;
}); });
@ -366,7 +370,7 @@ if (contest) {
.not($(this)) .not($(this))
.removeClass('active') .removeClass('active')
; ;
monaco.editor.setModelLanguage(editor.getModel(), 'ace_' + $(this).data('mode')); monaco.editor.setModelLanguage(editor.getModel(), $(this).data('mode'));
}); });
}); });
</script> </script>

88
views/problem_edit.ejs

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

31
views/user_edit.ejs

@ -1,5 +1,6 @@
<% this.title = '修改资料'; %> <% this.title = '修改资料'; %>
<% include header %> <% include header %>
<% include monaco-editor %>
<div class="padding"> <div class="padding">
<div class="ui <% if (error_info === '') { %>success<% } else { %>error<% } %> message" id="error" <% if (error_info === null) { %>hidden<% } %>> <div class="ui <% if (error_info === '') { %>success<% } else { %>error<% } %> message" id="error" <% if (error_info === null) { %>hidden<% } %>>
<% if (error_info !== null) { <% if (error_info !== null) {
@ -8,7 +9,8 @@
<p id="error_info"><%= error_info %></p> <p id="error_info"><%= error_info %></p>
<% } %> <% } %>
</div> </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"> <div class="field">
<label for="username">用户名</label> <label for="username">用户名</label>
<input type="text" id="username" name="username" value="<%= edited_user.username %>"<% if (!user.allowedManage) { %> readonly<% } %>> <input type="text" id="username" name="username" value="<%= edited_user.username %>"<% if (!user.allowedManage) { %> readonly<% } %>>
@ -40,9 +42,16 @@
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label for="information">个性签名</label> <label for="information">个性签名</label>
<textarea class="font-content" rows="5" id="information" name="information"><%= edited_user.information %></textarea> </div>
</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"> <div class="field">
<label class="ui header">密码</label> <label class="ui header">密码</label>
<input type="password" placeholder="原密码(留空则不修改)" name="old_password" id="old_password"> <input type="password" placeholder="原密码(留空则不修改)" name="old_password" id="old_password">
@ -80,12 +89,26 @@
<div style="text-align: center; margin-top: 30px; "> <div style="text-align: center; margin-top: 30px; ">
<button type="submit" class="ui blue labeled icon button"><i class="ui icon edit"></i>修改</button> <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> <a href="<%= syzoj.utils.makeUrl(['user', edited_user.id]) %>" class="ui labeled icon button"><i class="ui icon angle left"></i>返回</a>
</div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<script src="<%- lib('blueimp-md5/2.10.0/js/md5.min.js') %>"></script> <script src="<%- lib('blueimp-md5/2.10.0/js/md5.min.js') %>"></script>
<script type="text/javascript"> <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) { function make_md5(tag) {
if (tag.val()) { if (tag.val()) {
tag.val(md5(tag.val() + "syzoj2_xxx")); tag.val(md5(tag.val() + "syzoj2_xxx"));

Loading…
Cancel
Save