Merge branch 'dev' into virusdefender-dev

* dev:
  [前端]修正csrf.js    uploader.js  的参数设置 [CI SKIP]
  [前端]修改添加题目和添加用户页面, 添加新字段, 完善上传功能, 显示已上传文件列表
  [前端]修改用户列表get URL ,因为后端改动API [CI SKIP]
  [前端] 在add_problem完成了webuploader的配置,修改uploader.swf的位置,又放回原地了
  [前端]添加了一些字段,难度,标签,是否可见  [CI SKIP]
This commit is contained in:
virusdefender committed 2015-08-10 17:07:23 +08:00
commit 6eb54bec6c
13 files changed
+242 -71

No files matched your search

@@ -0,0 +1,45 @@
/* surrounding tag container */
.tag-editor {
list-style-type: none; padding: 0 5px 0 0; margin: 0; overflow: hidden; border: 1px solid #eee; cursor: text;
font: normal 14px sans-serif; color: #555; background: #fff;
}
/* core styles usually need no change */
.tag-editor li { display: block; float: left; overflow: hidden; margin: 3px 0; line-height: 1.5; }
.tag-editor div { float: left; padding: 0 4px; }
.tag-editor .placeholder { padding: 0 8px; color: #bbb; }
.tag-editor .tag-editor-spacer { padding: 0; width: 8px; overflow: hidden; color: transparent; background: none; }
.tag-editor input {
vertical-align: inherit; border: 0; outline: none; padding: 0; margin: 0; cursor: text;
font-family: inherit; font-weight: inherit; font-size: inherit; font-style: inherit;
box-shadow: none; background: none;
}
/* tag style */
.tag-editor .tag-editor-tag {
padding-left: 5px; color: #46799b; background: #e0eaf1; white-space: nowrap;
overflow: hidden; cursor: pointer; border-radius: 2px 0 0 2px;
}
/* delete icon */
.tag-editor .tag-editor-delete { background: #e0eaf1; cursor: pointer; padding-right: 5px; border-radius: 0 2px 2px 0; }
.tag-editor .tag-editor-delete i {
display: inline-block; width: 7px; height: 7px; vertical-align: middle; background: url(/static/img/delete.png) 0 0 no-repeat;
position: relative; top: -1px;
}
.tag-editor .tag-editor-delete:hover i { background-position: 0 -14px; }
.tag-editor .tag-editor-tag.active+.tag-editor-delete,
.tag-editor .tag-editor-tag.active+.tag-editor-delete i { background: none; cursor: text; }
.tag-editor .tag-editor-tag.active { background: none !important; }
/* jQuery UI autocomplete - code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css */
.ui-autocomplete { position: absolute; top: 0; left: 0; cursor: default; font-size: 14px; }
.ui-front { z-index: 9999; }
.ui-menu { list-style: none; padding: 1px; margin: 0; display: block; outline: none; }
.ui-menu .ui-menu-item a { text-decoration: none; display: block; padding: 2px .4em; line-height: 1.4; min-height: 0; /* support: IE7 */ }
.ui-widget-content { border: 1px solid #bbb; background: #fff; color: #555; }
.ui-widget-content a { color: #46799b; }
.ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus { background: #e0eaf1; }
.ui-helper-hidden-accessible { display: none; }
File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?
<svg id="svg2" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" y="0px" xml:space="preserve" height="7" viewBox="0 0 6.8000018 7.0000004" width="6.8" version="1.1" enable-background="new 0 0 16 16" x="0px">
<path id="path4" fill="#d40000" d="m3.9834,3.5,2.6753-2.6753c0.18847-0.18847,0.18847-0.49485,0-0.68332-0.18846-0.18847-0.49485-0.18847-0.68332,0l-2.6754,2.6751-2.6753-2.6752c-0.18847-0.18847-0.49485-0.18847-0.68332,0-0.18847,0.18846-0.18847,0.49485,0,0.68332l2.6751,2.6753-2.6751,2.6752c-0.18847,0.18847-0.18847,0.49485,0,0.68332s0.49485,0.18847,0.68332,0l2.6753-2.6752,2.6752,2.6753c0.18847,0.18847,0.49485,0.18847,0.68332,0s0.18847-0.49485,0-0.68332l-2.6752-2.6753z"/>
</svg>␍
+53 -12
View File
@@ -1,6 +1,5 @@
require(["jquery", "avalon", "editor", "uploader", "datetimepicker",
"validation"
],
"validation","tagEditor"],
function ($, avalon, editor, uploader) {
avalon.vmodels.add_contest = null;
$("#add-contest-form")
@@ -19,7 +18,7 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker",
}
}
},
description:{
description: {
validators: {
notEmpty: {
message: "请输入描述"
@@ -100,13 +99,20 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker",
})
.on("success.form.fv", function (e) {
e.preventDefault();
var data = {title: vm.title, description: vm.description, start_time: vm.startTime, end_time: vm.endTime,
password: vm.password, model: vm.model, open_rank: vm.openRank, problems:[]};
var data = {
title: vm.title, description: vm.description, start_time: vm.startTime, end_time: vm.endTime,
password: vm.password, model: vm.model, open_rank: vm.openRank, problems: []
};
for (var i = 0; i < vm.problems.length; i++) {
var problem = {title: vm.problems[i].title, description:vm.problems[i].description,
cpu:vm.problems[i].cpu, memory:vm.problems[i].memory,samples:[]};
var problem = {
title: vm.problems[i].title, description: vm.problems[i].description,
cpu: vm.problems[i].cpu, memory: vm.problems[i].memory, samples: []
};
for (var j = 0; j < vm.problems[i].samples.length; j++) {
problem.samples.push({input:vm.problems[i].samples[j].input, output:vm.problems[i].samples[j].output})
problem.samples.push({
input: vm.problems[i].samples[j].input,
output: vm.problems[i].samples[j].output
})
}
data.problems.push(problem);
}
@@ -119,11 +125,13 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker",
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
var editor1 = editor("#editor");
var vm = avalon.define({
$id: "add_contest",
title : "",
title: "",
description: "",
startTime: "",
endTime: "",
@@ -133,10 +141,43 @@ require(["jquery", "avalon", "editor", "uploader", "datetimepicker",
problems: [],
add_problem: function () {
var problem_id = make_id();
var problem={id: problem_id, title: "", cpu: "", memory: "", description: "",samples: [], webuploader: {}, visible: true};
var problem = {
id: problem_id,
title: "",
cpu: "",
memory: "",
description: "",
samples: [],
visible: true,
test_case_id: "",
testCaseList: [],
hint: "",
isVisible: false,
difficulty: 0,
tags: [],
tag: ""
};
vm.problems.push(problem);
uploader("#problem-" + problem_id + "-uploader","");
editor("#problem-" + problem_id + "-description")
var id = vm.problems.length - 1;
editor("#problem-" + problem_id + "-description");
var hinteditor = editor("#problem-" + problem_id +"-hint");
$("#problem-" + problem_id +"-tags").tagEditor();
uploader("#problem-" + problem_id + "-uploader", "/api/admin/test_case_upload/", function (file, respond) {
console.log(respond);
if (respond.code)
bs_alert(respond.data);
else {
vm.problems[id].test_case_id = respond.data.test_case_id;
vm.problems[id].uploadSuccess = true;
vm.problems[id].testCaseList = [];
for (var i = 0; i < respond.data.file_list.input.length; i++) {
vm.problems[id].push({
input: respond.data.file_list.input[i],
output: respond.data.file_list.output[i]
});
}
}
});
$("#add-contest-form").formValidation('addField', $('[name="problem_name[]"]'));
$("#add-contest-form").formValidation('addField', $('[name="cpu[]"]'));
$("#add-contest-form").formValidation('addField', $('[name="memory[]"]'));
+34 -14
View File
@@ -1,5 +1,5 @@
require(["jquery", "avalon", "editor", "uploader", "validation"],
function ($, avalon, editor, uploader) {
require(["jquery", "avalon", "editor", "uploader", "bs_alert", "tagEditor", "validation"],
function ($, avalon, editor, uploader, bs_alert) {
avalon.vmodels.add_problem = null;
$("#add-problem-form")
.formValidation({
@@ -68,18 +68,21 @@ require(["jquery", "avalon", "editor", "uploader", "validation"],
console.log(ajaxData);
});
var problemDiscription = editor("#problemDescription");
var testCaseUploader = uploader("#testCaseFile", "/admin/api/testCase");//{
/*auto: true,
swf: '/static/js/lib/webuploader/Uploader.swf',
server: 'http://webuploader.duapp.com/server/fileupload.php',
multiple:false,
accept: {
title: 'Zip',
extensions: 'zip',
mimeTypes: 'zip/*'
}*/
// });
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/",function(file, respond){
if (respond.code)
bs_alert(respond.data);
else{
vm.test_case_id = respond.data.test_case_id;
vm.uploadSuccess = true;
vm.testCaseList = [];
for (var i = 0; i < respond.data.file_list.input.length; i++)
{
vm.testCaseList.push({input: respond.data.file_list.input[i], output: respond.data.file_list.output[i]});
}
}
});
var hinteditor = editor("#hint");
$("#tags").tagEditor();
var vm = avalon.define({
$id: "add_problem",
title: "",
@@ -87,6 +90,22 @@ require(["jquery", "avalon", "editor", "uploader", "validation"],
cpu: 0,
memory: 0,
samples: [],
hint: "",
visible: false,
difficulty: 0,
tags: [],
tag: "",
test_case_id: "",
testCaseList: [],
uploadSuccess: false,
checkTag: function () {
alert("11");
if (event.keyCode == 13)
{
alert("You press the enter key!");
return false;
}
},
add_sample: function () {
vm.samples.push({input: "", output: "", "visible": true});
},
@@ -104,5 +123,6 @@ require(["jquery", "avalon", "editor", "uploader", "validation"],
return "展开";
}
});
avalon.scan();
});
+1 -1
View File
@@ -63,7 +63,7 @@ require(["jquery", "avalon", "csrf", "bs_alert", "validation"], function ($, ava
getPageData(1); //用户列表初始化
//Ajax get数据
function getPageData(page) {
var url = "/api/users/?paging=true&page=" + page + "&page_size=10";
var url = "/api/admin/users/?paging=true&page=" + page + "&page_size=10";
if (vm.showAdminOnly == true)
url += "&admin_type=1";
if (vm.key_word != "")
+1
View File
@@ -17,6 +17,7 @@ var require = {
csrf: "utils/csrf",
admin: "app/admin/admin",
chart: "lib/chart/Chart",
tagEditor: "lib/tagEditor/jquery.tag-editor.min",
//formValidation 不要在代码中单独使用,而是使用和修改utils/validation
base: "lib/formValidation/base",
+5
View File
@@ -0,0 +1,5 @@
// jQuery tagEditor v1.0.3
// https://github.com/Pixabay/jQuery-tagEditor
define("tagEditor", ["jquery"], function($){
!function(t){t.fn.autoGrowInput=function(e){return e=t.extend({maxWidth:250,minWidth:20,comfortZone:0},e),this.filter("input:text").each(function(){var i=(e.minWidth||t(this).width()," "),a=t(this),n=e.comfortZone?e.comfortZone:parseInt(.9*parseInt(t(this).css("fontSize"))),r=t("<dummy/>").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:a.css("fontSize"),fontFamily:a.css("fontFamily"),fontWeight:a.css("fontWeight"),letterSpacing:a.css("letterSpacing"),whiteSpace:"nowrap"}),o=function(){if(i!==(i=a.val())){r.html(i.replace(/&/g,"&amp;").replace(/\s/g,"&nbsp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"));var t=r.width()+n;t>e.maxWidth&&(t=e.maxWidth),t<e.minWidth&&(t=e.minWidth),t!=a.width()&&a.width(t)}};r.insertAfter(a),t(this).bind("keyup keydown blur focus autogrow",o)}),this},!function(t){t.fn.caret=function(t){var e=this[0],i="true"===e.contentEditable;if(0==arguments.length){if(window.getSelection){if(i){e.focus();var a=window.getSelection().getRangeAt(0),n=a.cloneRange();return n.selectNodeContents(e),n.setEnd(a.endContainer,a.endOffset),n.toString().length}return e.selectionStart}if(document.selection){if(e.focus(),i){var a=document.selection.createRange(),n=document.body.createTextRange();return n.moveToElementText(e),n.setEndPoint("EndToEnd",a),n.text.length}var t=0,r=e.createTextRange(),n=document.selection.createRange().duplicate(),o=n.getBookmark();for(r.moveToBookmark(o);0!==r.moveStart("character",-1);)t++;return t}return 0}if(-1==t&&(t=this[i?"text":"val"]().length),window.getSelection)i?(e.focus(),window.getSelection().collapse(e.firstChild,t)):e.setSelectionRange(t,t);else if(document.body.createTextRange)if(i){var r=document.body.createTextRange();r.moveToElementText(e),r.moveStart("character",t),r.collapse(!0),r.select()}else{var r=e.createTextRange();r.move("character",t),r.select()}return i||e.focus(),t}}(jQuery),t.fn.tagEditor=function(e,a,n){function r(e){if(8==e.which||46==e.which||e.ctrlKey&&88==e.which){var a=getSelection(),n=t(a.getRangeAt(0).commonAncestorContainer);if(n.hasClass("tag-editor")){var r=[],o=a.toString().split(n.prev().data("options").dregex);for(i=0;i<o.length;i++){var l=t.trim(o[i]);l&&r.push(l)}return t(".tag-editor-tag",n).each(function(){~t.inArray(t(this).html(),r)&&t(this).closest("li").find(".tag-editor-delete").click()}),!1}}}var o,l=t.extend({},t.fn.tagEditor.defaults,e),c=this;if(l.dregex=new RegExp("["+l.delimiter.replace("-","-")+"]","g"),"string"==typeof e){var s=[];return c.each(function(){var i=t(this),r=i.data("options"),o=i.next(".tag-editor");"getTags"==e?s.push({field:i[0],editor:o,tags:o.data("tags")}):"addTag"==e?(t('<li><div class="tag-editor-spacer">&nbsp;'+r.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>').appendTo(o).find(".tag-editor-tag").html('<input type="text" maxlength="'+r.maxLength+'">').addClass("active").find("input").val(a).blur(),"blur"!=n?o.click():t(".placeholder",o).remove()):"removeTag"==e?(t(".tag-editor-tag",o).filter(function(){return t(this).html()==a}).closest("li").find(".tag-editor-delete").click(),"blur"!=n&&o.click()):"destroy"==e&&i.css("display",r.elDisplay).removeData("options").next(".tag-editor").remove()}),s}return window.getSelection&&t(document).off("keydown.tag-editor").on("keydown.tag-editor",r),c.each(function(){function e(){!l.placeholder||c.length||t(".deleted, .placeholder, input",s).length||s.append('<li class="placeholder"><div>'+l.placeholder+"</div></li>")}function a(i){var a=c.toString();c=t(".tag-editor-tag:not(.deleted)",s).map(function(e,i){var a=t.trim(t(this).hasClass("active")?t(this).find("input").val():t(i).text());return a?a:void 0}).get(),s.data("tags",c),r.val(c.join(l.delimiter[0])),i||a!=c.toString()&&l.onChange(r,s,c),e()}function n(e){var n=e.closest("li"),o=e.val().replace(/ +/," ").split(l.dregex),d=e.data("old_tag"),g=c.slice(0);for(i in o)u=t.trim(o[i]).slice(0,l.maxLength),u&&(l.forceLowercase&&(u=u.toLowerCase()),l.beforeTagSave(r,s,g,d,u),~t.inArray(u,g)&&t(".tag-editor-tag",s).each(function(){t(this).html()==u&&t(this).closest("li").remove()}),g.push(u),n.before('<li><div class="tag-editor-spacer">&nbsp;'+l.delimiter[0]+'</div><div class="tag-editor-tag">'+u+'</div><div class="tag-editor-delete"><i></i></div></li>'));e.attr("maxlength",l.maxLength).removeData("old_tag").val("").focus(),a()}var r=t(this),c=[];l.elDisplay=r.css("display"),r.css("display","none");var s=t("<ul "+(l.clickDelete?'oncontextmenu="return false;" ':"")+'class="tag-editor"></ul>').insertAfter(r);r.data("options",l),s.append('<li style="width:.1px">&nbsp;</li>');var d='<li><div class="tag-editor-spacer">&nbsp;'+l.delimiter[0]+'</div><div class="tag-editor-tag"></div><div class="tag-editor-delete"><i></i></div></li>';s.click(function(e){if(!window.getSelection||""==getSelection()){if(o=!0,t("input:focus",s).blur(),!o)return!1;o=!0,t(".placeholder",s).remove();var i,a,n,r=99999;return t(".tag-editor-tag",s).each(function(){var o=Line truncated
});
+1 -1
View File
@@ -11,7 +11,7 @@ define("csrf",function(){
}
function csrfHeader(){
// jquery的请求
if(arguments.length == 1) {
if(arguments.length == 2) {
arguments[0].setRequestHeader("X-CSRFToken", get_cookie("csrftoken"));
}
// 百度webuploader 的请求
+12 -9
View File
@@ -1,20 +1,23 @@
define("uploader", ["webuploader"], function(webuploader){
function uploader(selector, server) {
return webuploader.create({
define("uploader", ["webuploader", "csrf"], function(webuploader,csrf){
function uploader(selector, server, onSuccess) {
var Webuploader= webuploader.create({
auto: true,
// swf文件路径
swf: "/js/Uploader.swf",
swf: "/static/img/Uploader.swf",
// 文件接收服务端。
server: server,
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: selector,
// 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
resize: false
resize: false,
uploadBeforeSend : csrf
});
Webuploader.on("uploadBeforeSend",csrf);
Webuploader.on("uploadSuccess", onSuccess);
return Webuploader;
}
return uploader;
});
+50 -19
View File
@@ -101,26 +101,40 @@
ms-duplex="problem.title">
</div>
</div>
<div class="col-md-6">
<label>cpu</label>
</div>
<div class="col-md-6">
<label>内存</label>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" name="cpu[]" class="form-control" ms-duplex="problem.cpu">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" name="memory[]" class="form-control" ms-duplex="problem.memory">
</div>
</div>
<label>题目描述</label>
<textarea ms-attr-id="problem-{{ problem.id }}-description" placeholder="这里输入内容"
ms-duplex="problem.description"></textarea>
<small ms-visible="problem.description==''" style="color:red">请填写题目描述</small>
<div class="form-group">
<label>提示</label>
<textarea ms-attr-id="problem-{{ problem.id }}-hint" placeholder="这里输入内容" ms-duplex="problem.hint"></textarea>
</div>
<div class="col-md-3">
<div class="form-group"><label>cpu</label>
<input type="number" name="cpu[]" class="form-control" ms-duplex="problem.cpu">
</div>
</div>
<div class="col-md-3">
<div class="form-group"><label>内存</label>
<input type="number" name="memory[]" class="form-control" ms-duplex="problem.memory">
</div>
</div>
<div class="col-md-3">
<div class="form-group"><label>难度</label>
<input type="number" name="difficulty[]" class="form-control" ms-duplex="problem.difficulty">
</div>
</div>
<div class="col-md-3 form-group">
<label>前台是否可见</label><br>
<label><input type="checkbox" ms-duplex-checked="problem.isVisible">
<small> 可见</small>
</label>
</div>
<div id="tag" class="col-md-12">
<label>标签</label><br>
<input type="text" ms-attr-id="problem-{{ problem.id }}-tags" >
</div>
<div class="col-md-12">
<label>样例</label>
@@ -156,7 +170,8 @@
<div class="col-md-6">
<div class="form-group">
<label>样例输出</label>
<textarea class="form-control" rows="5" ms-duplex="sample.output"></textarea>
<textarea class="form-control" rows="5"
ms-duplex="sample.output"></textarea>
</div>
</div>
</div>
@@ -164,7 +179,22 @@
</div>
</div>
<div class="col-md-12">
<label>测试数据</label>
<label>测试数据</label><br>
<small class="text-info">请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:<br>
1.in 1.out 2.in 2.out
</small>
<table class="table table-striped" ms-visible="uploadSuccess">
<tr>
<td>编号</td>
<td>输入文件名</td>
<td>输出文件名</td>
</tr>
<tr ms-repeat="testCaseList">
<td>{{$index}}</td>
<td>{{el.input}}</td>
<td>{{el.output}}</td>
</tr>
</table>
</div>
<div class="col-md-12">
<div class="form-group">
@@ -184,4 +214,5 @@
</form>
</div>
<script src="/static/js/app/admin/contest/contest.js"></script>
<script src="/static/js/app/admin/contest/contest.js"></script>
<link href="/static/css/tagEditor/jquery.tag-editor.css" rel="stylesheet">
+36 -15
View File
@@ -10,23 +10,37 @@
<textarea id="problemDescription" placeholder="这里输入内容" autofocus ms-duplex="description"></textarea>
<small ms-visible="description==''" style="color:red">请填写题目描述</small>
</div>
<div class="col-md-6">
<label>cpu</label>
<div class="form-group">
<label>提示</label>
<textarea id="hint" placeholder="这里输入内容" ms-duplex="hint"></textarea>
</div>
<div class="col-md-6">
<label>内存</label>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" name="cpu" class="form-control" ms-duplex="cpu">
<div class="col-md-3">
<div class="form-group"><label>cpu</label>
<input type="number" name="cpu" class="form-control" ms-duplex="cpu">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="text" name="memory" class="form-control" ms-duplex="memory">
<div class="col-md-3">
<div class="form-group"><label>内存</label>
<input type="number" name="memory" class="form-control" ms-duplex="memory">
</div>
</div>
<div class="col-md-12">
<div class="col-md-3">
<div class="form-group"><label>难度</label>
<input type="number" name="difficulty" class="form-control" ms-duplex="difficulty">
</div>
</div>
<div class="col-md-3 form-group">
<label>前台是否可见</label><br>
<label><input type="checkbox" ms-duplex-checked="visible">
<small> 可见</small>
</label>
</div>
<div id="tag" class="col-md-12">
<label>标签</label><br>
<input type="text" id="tags" onchange="console.log(a)">
</div>
<div class="col-md-12"><br>
<label>样例</label>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="add_sample()">添加</a>
@@ -60,8 +74,14 @@
</div>
</div>
</div>
<div class="col-md-12">
<label>测试数据</label>
<div class="col-md-12"><br>
<label>测试数据</label><br>
<small class="text-info">请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:<br>
1.in 1.out 2.in 2.out</small>
<table class="table table-striped" ms-visible="uploadSuccess">
<tr><td>编号</td><td>输入文件名</td><td>输出文件名</td></tr>
<tr ms-repeat="testCaseList"><td>{{$index}}</td><td>{{el.input}}</td><td>{{el.output}}</td></tr>
</table>
</div>
<div class="col-md-12">
<div class="form-group">
@@ -75,4 +95,5 @@
</form>
</div>
<script src="/static/js/app/admin/problem/add_problem.js"></script>
<script src="/static/js/app/admin/problem/add_problem.js"></script>
<link href="/static/css/tagEditor/jquery.tag-editor.css" rel="stylesheet">