diff --git a/contest/serializers.py b/contest/serializers.py index e0fde2a4..0ee0def5 100644 --- a/contest/serializers.py +++ b/contest/serializers.py @@ -110,4 +110,4 @@ class EditContestProblemSerializer(serializers.Serializer): class ContestPasswordVerifySerializer(serializers.Serializer): contest_id = serializers.IntegerField() - password = serializers.CharField(max_length=30) + password = serializers.CharField(max_length=30) \ No newline at end of file diff --git a/contest/views.py b/contest/views.py index 29c943a7..03285c99 100644 --- a/contest/views.py +++ b/contest/views.py @@ -16,10 +16,11 @@ from rest_framework.views import APIView from utils.shortcuts import (serializer_invalid_response, error_response, success_response, paginate, error_page, paginate_data) from account.models import SUPER_ADMIN, User -from account.decorators import login_required +from account.decorators import login_required, super_admin_required from group.models import Group from utils.cache import get_cache_redis from submission.models import Submission +from problem.models import Problem from .models import (Contest, ContestProblem, CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank) from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST @@ -90,8 +91,8 @@ class ContestAdminAPIView(APIView): try: # 超级管理员可以编辑所有的 contest = Contest.objects.get(id=data["id"]) - if request.user.admin_type != SUPER_ADMIN: - contest = contest.get(created_by=request.user) + if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user: + return error_response(u"无权访问!") except Contest.DoesNotExist: return error_response(u"该比赛不存在!") try: @@ -144,6 +145,18 @@ class ContestAdminAPIView(APIView): --- response_serializer: ContestSerializer """ + contest_id = request.GET.get("contest_id", None) + if contest_id: + try: + # 普通管理员只能获取自己创建的题目 + # 超级管理员可以获取全部的题目 + contest = Contest.objects.get(id=contest_id) + if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user: + return error_response(u"题目不存在") + return success_response(ContestSerializer(contest).data) + except Contest.DoesNotExist: + return error_response(u"题目不存在") + if request.user.admin_type == SUPER_ADMIN: contest = Contest.objects.all().order_by("-create_time") else: @@ -171,8 +184,8 @@ class ContestProblemAdminAPIView(APIView): data = serializer.data try: contest = Contest.objects.get(id=data["contest_id"]) - if request.user.admin_type != SUPER_ADMIN: - contest = contest.get(created_by=request.user) + if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user: + return error_response(u"比赛不存在") except Contest.DoesNotExist: return error_response(u"比赛不存在") contest_problem = ContestProblem.objects.create(title=data["title"], @@ -186,8 +199,7 @@ class ContestProblemAdminAPIView(APIView): created_by=request.user, hint=data["hint"], contest=contest, - sort_index=data["sort_index"], - score=data["score"]) + sort_index=data["sort_index"]) return success_response(ContestProblemSerializer(contest_problem).data) else: return serializer_invalid_response(serializer) @@ -221,7 +233,6 @@ class ContestProblemAdminAPIView(APIView): contest_problem.hint = data["hint"] contest_problem.visible = data["visible"] contest_problem.sort_index = data["sort_index"] - contest_problem.score = data["score"] contest_problem.last_update_time = now() contest_problem.save() return success_response(ContestProblemSerializer(contest_problem).data) @@ -238,8 +249,8 @@ class ContestProblemAdminAPIView(APIView): if contest_problem_id: try: contest_problem = ContestProblem.objects.get(id=contest_problem_id) - if request.user.admin_type != SUPER_ADMIN: - contest_problem = contest_problem.get(created_by=request.user) + if request.user.admin_type != SUPER_ADMIN and contest_problem.created_by != request.user: + return error_response(u"比赛题目不存在") return success_response(ContestProblemSerializer(contest_problem).data) except ContestProblem.DoesNotExist: return error_response(u"比赛题目不存在") @@ -261,6 +272,26 @@ class ContestProblemAdminAPIView(APIView): return paginate(request, contest_problems, ContestProblemSerializer) +class MakeContestProblemPublicAPIView(APIView): + @super_admin_required + def post(self, request): + problem_id = request.data.get("problem_id", -1) + try: + problem = ContestProblem.objects.get(id=problem_id) + except ContestProblem.DoesNotExist: + return error_response(u"比赛不存在") + Problem.objects.create(title=problem.title, description=problem.description, + input_description=problem.input_description, + output_description=problem.output_description, + samples=problem.samples, + test_case_id=problem.test_case_id, + hint=problem.hint, created_by=problem.created_by, + time_limit=problem.time_limit, memory_limit=problem.memory_limit, + visible=False, difficulty=-1, source=problem.contest.title) + return success_response(u"创建成功") + + + class ContestPasswordVerifyAPIView(APIView): @login_required def post(self, request): diff --git a/mq/scripts/mq.py b/mq/scripts/mq.py index 7ba83fba..411b1033 100644 --- a/mq/scripts/mq.py +++ b/mq/scripts/mq.py @@ -10,7 +10,7 @@ from judge.judger.result import result from submission.models import Submission from problem.models import Problem from utils.cache import get_cache_redis -from contest.models import ContestProblem, Contest, ContestSubmission, CONTEST_UNDERWAY, ContestRank +from contest.models import ContestProblem, Contest, CONTEST_UNDERWAY, ContestRank from account.models import User logger = logging.getLogger("app_info") diff --git a/oj/urls.py b/oj/urls.py index dfbe669f..53283fe2 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -11,7 +11,8 @@ from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterA from announcement.views import AnnouncementAdminAPIView from contest.views import (ContestAdminAPIView, ContestProblemAdminAPIView, - ContestPasswordVerifyAPIView, ContestTimeAPIView) + ContestPasswordVerifyAPIView, ContestTimeAPIView, + MakeContestProblemPublicAPIView) from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView, JoinGroupAPIView, JoinGroupRequestAdminAPIView) @@ -65,6 +66,7 @@ urlpatterns = [ url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"), url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"), + url(r'^api/admin/contest_problem/public/', MakeContestProblemPublicAPIView.as_view(), name="make_contest_problem_public"), url(r'^api/admin/test_case_upload/$', TestCaseUploadAPIView.as_view(), name="test_case_upload_api"), url(r'^api/admin/tag/$', ProblemTagAdminAPIView.as_view(), name="problem_tag_admin_api"), url(r'^api/admin/join_group_request/$', JoinGroupRequestAdminAPIView.as_view(), diff --git a/problem/views.py b/problem/views.py index e48b8a5c..301aa25e 100644 --- a/problem/views.py +++ b/problem/views.py @@ -136,8 +136,8 @@ class ProblemAdminAPIView(APIView): # 普通管理员只能获取自己创建的题目 # 超级管理员可以获取全部的题目 problem = Problem.objects.get(id=problem_id) - if request.user.admin_type != SUPER_ADMIN: - problem = problem.get(created_by=request.user) + if request.user.admin_type != SUPER_ADMIN and problem.created_by != request.user: + return error_response(u"题目不存在") return success_response(ProblemSerializer(problem).data) except Problem.DoesNotExist: return error_response(u"题目不存在") @@ -248,17 +248,31 @@ class TestCaseUploadAPIView(APIView): "output_name": str(i + 1) + ".out", "output_md5": md5.hexdigest(), "striped_output_md5": striped_md5.hexdigest(), + "input_size": os.path.getsize(test_case_dir + str(i + 1) + ".in"), "output_size": os.path.getsize(test_case_dir + str(i + 1) + ".out")} # 写入配置文件 with open(test_case_dir + "info", "w") as f: f.write(json.dumps(file_info)) return success_response({"test_case_id": problem_test_dir, - "file_list": {"input": l[0::2], - "output": l[1::2]}}) + "file_list": file_info["test_cases"]}) else: return error_response(u"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩") + def get(self, request): + test_case_id = request.GET.get("test_case_id", None) + if not test_case_id: + return error_response(u"参数错误") + test_case_config = settings.TEST_CASE_DIR + test_case_id + "/info" + try: + f = open(test_case_config) + config = json.loads(f.read()) + f.close() + except Exception as e: + return error_response(u"读取测试用例出错") + return success_response({"file_list": config["test_cases"]}) + + def problem_list_page(request, page=1): """ diff --git a/static/src/js/app/admin/admin.js b/static/src/js/app/admin/admin.js index 6ef30f62..72c8fd6b 100644 --- a/static/src/js/app/admin/admin.js +++ b/static/src/js/app/admin/admin.js @@ -52,10 +52,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, { name: "首页", children: [{name: "主页", hash: "#index/index"}] }, - { - name: "通用", - children: [{name: "公告管理", hash: "#announcement/announcement"}] - }, { name: "比赛管理", children: [{name: "比赛列表", hash: "#contest/contest_list"}, @@ -72,12 +68,14 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, $id: "admin", template_url: "template/" + hash + ".html", username: "", + adminType: 1, groupId: -1, problemId: -1, adminNavList: [], - $contestMode: -1, - $problemId: -1, - $contestId: -1, + + contestId: -1, + contestProblemStatus: "edit", + hide_loading: function () { $("#loading-gif").hide(); }, @@ -94,6 +92,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, success: function(data){ if(!data.code){ vm.username = data.data.username; + vm.adminType = data.data.admin_type; if (data.data.admin_type == 2){ vm.adminNavList = superAdminNav; } @@ -113,24 +112,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, vm.template_url = "template/group/group.html"; }); - vm.$watch("showContestProblemPage", function (problemId, contestId, contestMode) { - vm.$problemId = problemId; - vm.$contestId = contestId; - vm.$contestMode = contestMode - vm.template_url = "template/contest/edit_problem.html"; - }); - - vm.$watch("showContestListPage", function () { - vm.template_url = "template/contest/contest_list.html"; - }); - - vm.$watch("showContestSubmissionPage", function (problemId, contestId, contestMode) { - vm.$problemId = problemId; - vm.$contestId = contestId; - vm.$contestMode = contestMode - vm.template_url = "template/contest/submission_list.html"; - }); - avalon.scan(); window.onhashchange = function () { diff --git a/static/src/js/app/admin/contest/addContest.js b/static/src/js/app/admin/contest/addContest.js index b05ba4ee..9cbd57dd 100644 --- a/static/src/js/app/admin/contest/addContest.js +++ b/static/src/js/app/admin/contest/addContest.js @@ -104,6 +104,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组"); return; } + vm.allGroups = []; for (var i = 0; i < data.data.length; i++) { var item = data.data[i]; item["isSelected"] = false; diff --git a/static/src/js/app/admin/contest/contestList.js b/static/src/js/app/admin/contest/contestList.js index 7eb47fad..885cdea5 100644 --- a/static/src/js/app/admin/contest/contestList.js +++ b/static/src/js/app/admin/contest/contestList.js @@ -20,6 +20,15 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", search: function () { getPage(1); avalon.vmodels.contestListPager.currentPage = 1; + }, + + editContest: function(contestId){ + avalon.vmodels.admin.contestId = contestId; + avalon.vmodels.admin.template_url = "template/contest/edit_contest.html"; + }, + showContestProblems: function(contestId){ + avalon.vmodels.admin.contestId = contestId; + avalon.vmodels.admin.template_url = "template/contest/problem_list.html"; } }) } @@ -42,7 +51,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", success: function (data) { if (!data.code) { vm.contestList = data.data.results; - vm.announcementList = data.data.results; avalon.vmodels.contestListPager.totalPage = data.data.total_page; } else { diff --git a/static/src/js/app/admin/contest/contestList1.js b/static/src/js/app/admin/contest/contestList1.js deleted file mode 100644 index 07ddc1e1..00000000 --- a/static/src/js/app/admin/contest/contestList1.js +++ /dev/null @@ -1,321 +0,0 @@ -require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator"], function ($, avalon, csrfTokenHeader, bsAlert, editor) { - - avalon.ready(function () { - - $("#edit-contest-form").validator().on('submit', function (e) { - if (!e.isDefaultPrevented()) { - e.preventDefault(); - var ajaxData = { - id: vm.contestList[vm.editingContestId - 1].id, - title: vm.editTitle, - description: vm.editDescription, - mode: vm.editMode, - contest_type: 0, - real_time_rank: vm.editRealTimeRank, - show_user_submission: vm.editShowSubmission, - start_time: vm.editStartTime, - end_time: vm.editEndTime, - visible: vm.editVisible - }; - - var selectedGroups = []; - if (!vm.isGlobal) { - for (var i = 0; i < vm.allGroups.length; i++) { - if (vm.allGroups[i].isSelected) { - selectedGroups.push(vm.allGroups[i].id); - } - } - ajaxData.groups = selectedGroups; - } - else { - if (vm.editPassword) { - ajaxData.password = vm.editPassword; - ajaxData.contest_type = 2; - } - else - ajaxData.contest_type = 1; - } - if (!vm.isGlobal && !selectedGroups.length) { - bsAlert("你没有选择参赛用户!"); - return false; - } - if (vm.editDescription == "") { - bsAlert("比赛描述不能为空!"); - return false; - } - - $.ajax({ // modify contest info - beforeSend: csrfTokenHeader, - url: "/api/admin/contest/", - dataType: "json", - contentType: "application/json;charset=UTF-8", - data: JSON.stringify(ajaxData), - method: "put", - success: function (data) { - if (!data.code) { - bsAlert("修改成功!"); - vm.editingContestId = 0; // Hide the editor - vm.getPage(1); // Refresh the contest list - } - else { - bsAlert(data.data); - } - } - }); - } - return false; - }); - - if (avalon.vmodels.contestList) { - // this page has been loaded before, so set the default value - var vm = avalon.vmodels.contestList; - vm.contestList = []; - } - else { - var vm = avalon.define({ - $id: "contestList", - contestList: [], - previousPage: 0, - nextPage: 0, - page: 1, - totalPage: 1, - showVisibleOnly: false, - keyword: "", - editingContestId: 0, - editTitle: "", - editDescription: "", - editProblemList: [], - editPassword: "", - editStartTime: "", - editEndTime: "", - editMode: "", - editShowSubmission: false, - editVisible: false, - editRealTimeRank: true, - editingProblemContestIndex: 0, - isGlobal: true, - allGroups: [], - showGlobalViewRadio: true, - admin_type: 1, - getNext: function () { - if (!vm.nextPage) - return; - getPageData(vm.page + 1); - }, - getPrevious: function () { - if (!vm.previousPage) - return; - getPageData(vm.page - 1); - }, - getBtnClass: function (btn) { - if (btn == "next") { - return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - else { - return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - }, - getPage: function (page_index) { - getPageData(page_index); - }, - showEditContestArea: function (contestId) { - if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?")) - return; - if (contestId == vm.editingContestId) - vm.editingContestId = 0; - else { - vm.editingContestId = contestId; - vm.editTitle = vm.contestList[contestId - 1].title; - vm.editPassword = vm.contestList[contestId - 1].password; - vm.editStartTime = vm.contestList[contestId - 1].start_time.substring(0, 16).replace("T", " "); - vm.editEndTime = vm.contestList[contestId - 1].end_time.substring(0, 16).replace("T", " "); - vm.editMode = vm.contestList[contestId - 1].mode; - vm.editVisible = vm.contestList[contestId - 1].visible; - vm.editRealTimeRank = vm.contestList[contestId - 1].real_time_rank; - if (vm.contestList[contestId - 1].contest_type == 0) { //contest type == 0, contest in group - vm.isGlobal = false; - for (var i = 0; i < vm.allGroups.length; i++) { - vm.allGroups[i].isSelected = false; - } - for (var i = 0; i < vm.contestList[contestId - 1].groups.length; i++) { - var id = parseInt(vm.contestList[contestId - 1].groups[i]); - - for (var index = 0; vm.allGroups[index]; index++) { - if (vm.allGroups[index].id == id) { - vm.allGroups[index].isSelected = true; - break; - } - } - } - } - else { - vm.isGlobal = true; - } - vm.editShowSubmission = vm.contestList[contestId - 1].show_user_submission; - editor("#editor").setValue(vm.contestList[contestId - 1].description); - vm.editingProblemContestIndex = 0; - } - }, - showEditProblemArea: function (contestId) { - if (vm.editingProblemContestIndex == contestId) { - vm.editingProblemContestIndex = 0; - return; - } - if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?")) { - return; - } - $.ajax({ // Get the problem list of current contest - beforeSend: csrfTokenHeader, - url: "/api/admin/contest_problem/?contest_id=" + vm.contestList[contestId - 1].id, - method: "get", - dataType: "json", - success: function (data) { - if (!data.code) { - vm.editProblemList = data.data; - } - else { - bsAlert(data.data); - } - } - }); - vm.editingContestId = 0; - vm.editingProblemContestIndex = contestId; - vm.editMode = vm.contestList[contestId - 1].mode; - }, - addProblem: function () { - vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode); - }, - showProblemEditPage: function (el) { - vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode); - }, - showSubmissionPage: function (el) { - var problemId = 0 - if (el) - problemId = el.id; - vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode); - }, - addToProblemList: function (problem) { - var ajaxData = { - title: problem.title, - description: problem.description, - time_limit: problem.time_limit, - memory_limit: problem.memory_limit, - samples: [], - test_case_id: problem.test_case_id, - hint: problem.hint, - source: problem.contest.title, - visible: false, - tags: [], - input_description: problem.input_description, - output_description: problem.output_description, - difficulty: 0 - }; - for (var i = 0; i < problem.samples.length; i++) { - ajaxData.samples.push({input: problem.samples[i].input, output: problem.samples[i].output}) - } - $.ajax({ - beforeSend: csrfTokenHeader, - url: "/api/admin/problem/", - dataType: "json", - data: JSON.stringify(ajaxData), - method: "post", - contentType: "application/json;charset=UTF-8", - success: function (data) { - if (!data.code) { - bsAlert("题目添加成功!题目现在处于隐藏状态,请到题目列表手动修改,并添加分类和难度信息!"); - } - else { - bsAlert(data.data); - } - } - }); - } - }); - vm.$watch("showVisibleOnly", function () { - getPageData(1); - }) - } - getPageData(1); - - //init time picker - $("#contest_start_time").datetimepicker({ - format: "yyyy-mm-dd hh:ii", - minuteStep: 5, - weekStart: 1, - language: "zh-CN" - }); - $("#contest_end_time").datetimepicker({ - format: "yyyy-mm-dd hh:ii", - minuteStep: 5, - weekStart: 1, - language: "zh-CN" - }); - - function getPageData(page) { - var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10"; - if (vm.showVisibleOnly) - url += "&visible=true" - if (vm.keyword != "") - url += "&keyword=" + vm.keyword; - $.ajax({ - url: url, - dataType: "json", - method: "get", - success: function (data) { - if (!data.code) { - vm.contestList = data.data.results; - vm.totalPage = data.data.total_page; - vm.previousPage = data.data.previous_page; - vm.nextPage = data.data.next_page; - vm.page = page; - } - else { - bsAlert(data.data); - } - } - }); - } - - // Get group list - $.ajax({ - url: "/api/user/", - method: "get", - dataType: "json", - success: function (data) { - if (!data.code) { - var admin_type = data.data.admin_type; - vm.admin_type = admin_type; - if (data.data.admin_type == 1) { - vm.isGlobal = false; - vm.showGlobalViewRadio = false; - } - } - $.ajax({ - url: "/api/admin/group/", - method: "get", - dataType: "json", - success: function (data) { - if (!data.code) { - if (!data.data.length) { - - if (admin_type != 2) - bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组"); - return; - } - for (var i = 0; i < data.data.length; i++) { - var item = data.data[i]; - item["isSelected"] = false; - vm.allGroups.push(item); - } - } - else { - bsAlert(data.data); - } - } - }); - } - }); - - }); - avalon.scan(); -}); \ No newline at end of file diff --git a/static/src/js/app/admin/contest/contestProblemList.js b/static/src/js/app/admin/contest/contestProblemList.js new file mode 100644 index 00000000..c2dc4330 --- /dev/null +++ b/static/src/js/app/admin/contest/contestProblemList.js @@ -0,0 +1,64 @@ +require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) { + + avalon.ready(function () { + if (avalon.vmodels.contestProblemList) { + vm = avalon.vmodels.contestProblemList; + } + else { + var vm = avalon.define({ + $id: "contestProblemList", + problemList: [], + + adminType: avalon.vmodels.admin.adminType, + + showEditProblemPage: function (problemId) { + avalon.vmodels.admin.contestProblemStatus = "edit"; + avalon.vmodels.admin.problemId = problemId; + avalon.vmodels.admin.template_url = "template/contest/edit_problem.html"; + }, + addProblem: function(){ + avalon.vmodels.admin.contestProblemStatus = "add"; + avalon.vmodels.admin.template_url = "template/contest/edit_problem.html"; + }, + goBack: function(){ + avalon.vmodels.admin.template_url = "template/contest/contest_list.html" + }, + + makeProblemPublic: function(problem){ + $.ajax({ + url: "/api/admin/contest_problem/public/", + method: "post", + dataType: "json", + data: {"problem_id": problem.id}, + success: function(response){ + if(response.code){ + bsAlert(response.data); + } + else{ + problem.is_public = true; + alert("公开题目成功,现在处于隐藏状态,请添加标签难度等信息。"); + } + } + }) + } + }); + } + + $.ajax({ + url: "/api/admin/contest_problem/?contest_id=" + avalon.vmodels.admin.contestId, + dataType: "json", + method: "get", + success: function (data) { + if (!data.code) { + vm.problemList = data.data; + } + else { + bsAlert(data.data); + } + } + }); + + avalon.scan(); + }); + +}); diff --git a/static/src/js/app/admin/contest/editContest.js b/static/src/js/app/admin/contest/editContest.js new file mode 100644 index 00000000..f34e2614 --- /dev/null +++ b/static/src/js/app/admin/contest/editContest.js @@ -0,0 +1,175 @@ +require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker", + "validator", "editorComponent"], + function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { + + $("#edit-contest-form").validator().on('submit', function (e) { + if (!e.isDefaultPrevented()) { + e.preventDefault(); + var ajaxData = { + id: avalon.vmodels.admin.contestId, + title: vm.title, + description: avalon.vmodels.contestDescriptionEditor.content, + contest_type: 0, + real_time_rank: vm.realTimeRank, + start_time: vm.startTime, + end_time: vm.endTime, + visible: vm.visible + }; + + var selectedGroups = []; + if (!vm.isGlobal) { + for (var i = 0; i < vm.allGroups.length; i++) { + if (vm.allGroups[i].isSelected) { + selectedGroups.push(vm.allGroups[i].id); + } + } + ajaxData.groups = selectedGroups; + } + else { + if (vm.password) { + ajaxData.password = vm.password; + ajaxData.contest_type = 2; + } + else + ajaxData.contest_type = 1; + } + if (!vm.isGlobal && !selectedGroups.length) { + bsAlert("你没有选择参赛用户!"); + return false; + } + if (ajaxData.description.trim() == "") { + bsAlert("比赛描述不能为空!"); + return false; + } + $.ajax({ + url: "/api/admin/contest/", + dataType: "json", + contentType: "application/json;charset=UTF-8", + data: JSON.stringify(ajaxData), + method: "put", + success: function (data) { + if (!data.code) { + bsAlert("修改成功!"); + vm.showContestListPage(); + } + else { + bsAlert(data.data); + } + } + }); + } + return false; + }); + + if (avalon.vmodels.edit_contest) + var vm = avalon.vmodels.edit_contest; + else + var vm = avalon.define({ + $id: "edit_contest", + title: "", + startTime: "", + endTime: "", + password: "", + isGlobal: true, + allGroups: [], + showGlobalViewRadio: true, + realTimeRank: true, + visible: false, + showContestListPage: function () { + avalon.vmodels.admin.template_url = "template/contest/contest_list.html"; + }, + + contestDescriptionEditor: { + editorId: "contest-description-editor", + placeholder: "比赛介绍内容" + } + }); + avalon.scan(); + $.ajax({ + url: "/api/user/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + var admin_type = data.data.admin_type; + if (data.data.admin_type == 1) { + vm.isGlobal = false; + vm.showGlobalViewRadio = false; + } + } + $.ajax({ + url: "/api/admin/group/", + method: "get", + dataType: "json", + success: function (data) { + if (!data.code) { + if (!data.data.length) { + if (admin_type != 2) + bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组"); + return; + } + vm.allGroups = []; + for (var i = 0; i < data.data.length; i++) { + var item = data.data[i]; + item.isSelected = false; + vm.allGroups.push(item); + } + $.ajax({ + url: "/api/admin/contest/?contest_id=" + avalon.vmodels.admin.contestId, + method: "get", + dataType: "json", + success: function (data) { + if (data.code) { + bsAlert(data.data); + } + else { + var contest = data.data; + vm.title = contest.title; + avalon.vmodels.contestDescriptionEditor.content = contest.description; + vm.visible = contest.visible; + vm.realTimeRank = contest.real_time_rank; + vm.startTime = contest.start_time.substring(0, 16).replace("T", " "); + vm.endTime = contest.end_time.substring(0, 16).replace("T", " "); + if (contest.contest_type == 0) { //contest_type == 0, 小组内比赛 + vm.isGlobal = false; + for (var i = 0; i < vm.allGroups.length; i++) { + vm.allGroups[i].isSelected = false; + } + for (var i = 0; i < contest.groups.length; i++) { + var id = contest.groups[i]; + for (var index = 0; vm.allGroups[index]; index++) { + if (vm.allGroups[index].id == id) { + vm.allGroups[index].isSelected = true; + break; + } + } + } + } + else { + vm.isGlobal = true; + } + } + } + }); + } + else { + bsAlert(data.data); + } + } + }); + } + }); + + $("#contest_start_time").datetimepicker({ + format: "yyyy-mm-dd hh:ii", + minuteStep: 5, + weekStart: 1, + language: "zh-CN" + }); + $("#contest_end_time").datetimepicker({ + format: "yyyy-mm-dd hh:ii", + minuteStep: 5, + weekStart: 1, + language: "zh-CN" + }); + }); \ No newline at end of file diff --git a/static/src/js/app/admin/contest/editProblem.js b/static/src/js/app/admin/contest/editProblem.js index 81c69316..31b2560a 100644 --- a/static/src/js/app/admin/contest/editProblem.js +++ b/static/src/js/app/admin/contest/editProblem.js @@ -1,22 +1,22 @@ -require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI"], +require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "editorComponent"], function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { avalon.ready(function () { $("#edit-problem-form").validator() .on('submit', function (e) { - if (!e.isDefaultPrevented()){ + if (!e.isDefaultPrevented()) { e.preventDefault(); if (vm.testCaseId == "") { bsAlert("你还没有上传测试数据!"); return false; } - if (vm.description == "") { + if (avalon.vmodels.contestProblemDescriptionEditor.content == "") { bsAlert("题目描述不能为空!"); return false; } - if (vm.timeLimit < 1000 || vm.timeLimit > 5000) { - bsAlert("保证时间限制是一个1000-5000的合法整数"); + if (vm.timeLimit < 30 || vm.timeLimit > 5000) { + bsAlert("保证时间限制是一个30-5000的合法整数"); return false; } if (vm.samples.length == 0) { @@ -31,33 +31,34 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } var ajaxData = { title: vm.title, - description: vm.description, + description: avalon.vmodels.contestProblemDescriptionEditor.content, time_limit: vm.timeLimit, memory_limit: vm.memoryLimit, samples: [], test_case_id: vm.testCaseId, - hint: vm.hint, - visible: vm.visible, - contest_id: avalon.vmodels.admin.$contestId, - input_description: vm.inputDescription, + hint: avalon.vmodels.contestProblemHintEditor.content, + visible: vm.visible, + contest_id: avalon.vmodels.admin.contestId, + input_description: vm.inputDescription, output_description: vm.outputDescription, - sort_index: vm.sortIndex, + sort_index: vm.sortIndex }; - if (vm.contestMode == '2') { - if (!vm.score) { - bsAlert("请输入有效的分值!") - return false; - } - ajaxData.score = vm.score; + + if (avalon.vmodels.admin.contestProblemStatus == "edit") { + var method = "put"; + ajaxData["id"] = avalon.vmodels.admin.problemId; + var alertContent = "题目编辑成功"; } - var method = "post"; - if (avalon.vmodels.admin.$problemId) { - method = "put"; - ajaxData.id = avalon.vmodels.admin.$problemId; + else{ + var method = "post"; + var alertContent = "题目创建成功"; } for (var i = 0; i < vm.samples.$model.length; i++) { - ajaxData.samples.push({input: vm.samples.$model[i].input, output: vm.samples.$model[i].output}); + ajaxData.samples.push({ + input: vm.samples.$model[i].input, + output: vm.samples.$model[i].output + }); } $.ajax({ @@ -69,8 +70,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE contentType: "application/json;charset=UTF-8", success: function (data) { if (!data.code) { - bsAlert("题目编辑成功!"); - vm.goBack(true); + bsAlert(alertContent); } else { bsAlert(data.data); @@ -82,93 +82,101 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } }); - if (!avalon.vmodels.editProblem) - var vm = avalon.define({ - $id: "editProblem", - title: "", - description: "", - timeLimit: 0, - memoryLimit: 0, - samples: [], - hint: "", - sortIndex: "", - visible: true, - inputDescription: "", - outputDescription: "", - testCaseIdd: "", - contestMode: 0, - score: 1, - uploadSuccess: false, - testCaseList: [], - addSample: function () { - vm.samples.push({input: "", output: "", "visible": true}); - }, - delSample: function (sample) { - if (confirm("你确定要删除么?")) { - vm.samples.remove(sample); - } - }, - toggleSample: function (sample) { - sample.visible = !sample.visible; - }, - getBtnContent: function (item) { - if (item.visible) - return "折叠"; - return "展开"; - }, - goBack: function(check){ - if (check||confirm("这将丢失所有的改动,确定要继续么?")) { - vm.$fire("up!showContestListPage"); - } - } - }); - else - vm = avalon.vmodels.editProblem; + if (!avalon.vmodels.editProblem) + var vm = avalon.define({ + $id: "editProblem", + title: "", + description: "", + timeLimit: 1000, + memoryLimit: 128, + samples: [], + hint: "", + sortIndex: "", + visible: true, + inputDescription: "", + outputDescription: "", + testCaseId: "", + testCaseList: [], + uploadSuccess: false, + + contestProblemDescriptionEditor: { + editorId: "contest-problem-description-editor", + placeholder: "题目描述" + }, + contestProblemHintEditor: { + editorId: "contest-problem-hint-editor", + placeholder: "提示" + }, + + addSample: function () { + vm.samples.push({input: "", output: "", "visible": true}); + }, + + delSample: function (sample) { + if (confirm("你确定要删除么?")) { + vm.samples.remove(sample); + } + }, + + toggleSample: function (sample) { + sample.visible = !sample.visible; + }, + + getBtnContent: function (item) { + if (item.visible) + return "折叠"; + return "展开"; + }, + + goBack: function (check) { + avalon.vmodels.admin.template_url = "template/contest/problem_list.html"; + } + }); + else { + var vm = avalon.vmodels.editProblem; + } - var hintEditor = editor("#hint"); - var descriptionEditor = editor("#problemDescription"); var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) { if (response.code) bsAlert(response.data); else { vm.testCaseId = response.data.test_case_id; - vm.uploadSuccess = true; vm.testCaseList = []; - for (var i = 0; i < response.data.file_list.input.length; i++) { + for(var key in response.data.file_list){ vm.testCaseList.push({ - input: response.data.file_list.input[i], - output: response.data.file_list.output[i] - }); + input: response.data.file_list[key].input_name, + output: response.data.file_list[key].output_name + }) } + vm.uploadSuccess = true; bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); } }); - vm.contestMode = avalon.vmodels.admin.$contestMode; - if (avalon.vmodels.admin.$problemId){ + if (avalon.vmodels.admin.contestProblemStatus == "edit") { $.ajax({ - url: "/api/admin/contest_problem/?contest_problem_id=" + avalon.vmodels.admin.$problemId, + url: "/api/admin/contest_problem/?contest_problem_id=" + avalon.vmodels.admin.problemId, method: "get", dataType: "json", success: function (data) { if (data.code) { bsAlert(data.data); } - else { // Edit mode load the problem data + else { var problem = data.data; - vm.testCaseList = []; - vm.sortIndex = problem.sort_index; - vm.title = problem.title; - vm.description = problem.description; - vm.timeLimit = problem.time_limit; - vm.memoryLimit = problem.memory_limit; - vm.hint = problem.hint; - vm.visible = problem.visible; - vm.inputDescription = problem.input_description; + vm.testCaseList = []; + vm.sortIndex = problem.sort_index; + vm.title = problem.title; + avalon.vmodels.contestProblemDescriptionEditor.content = problem.description; + vm.timeLimit = problem.time_limit; + vm.memoryLimit = problem.memory_limit; + vm.hint = problem.hint; + vm.visible = problem.visible; + vm.inputDescription = problem.input_description; vm.outputDescription = problem.output_description; - vm.score = problem.score; - vm.samples = []; - vm.testCaseId = problem.test_case_id; + vm.score = problem.score; + vm.testCaseId = problem.test_case_id; + vm.samples = []; for (var i = 0; i < problem.samples.length; i++) { vm.samples.push({ input: problem.samples[i].input, @@ -176,26 +184,32 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE visible: false }) } - hintEditor.setValue(vm.hint); - descriptionEditor.setValue(vm.description); + avalon.vmodels.contestProblemHintEditor.content = problem.hint; + $.ajax({ + url: "/api/admin/test_case_upload/?test_case_id=" + vm.testCaseId, + method: "get", + dataType: "json", + success: function(response){ + if(response.code){ + bsAlert(response.data); + } + else { + vm.testCaseList = []; + for (var key in response.data.file_list) { + vm.testCaseList.push({ + input: response.data.file_list[key].input_name, + output: response.data.file_list[key].output_name + }) + } + vm.uploadSuccess = true; + } + } + }) } } }); - } - else { //Create new problem Set default values - vm.testCaseList = []; - vm.title = ""; - vm.timeLimit = 1000; - vm.memoryLimit = 256; - vm.samples = []; - vm.visible = true; - vm.inputDescription = ""; - vm.outputDescription = ""; - vm.testCaseId = ""; - vm.sortIndex = ""; - vm.score = 0; - hintEditor.setValue(""); - descriptionEditor.setValue(""); + + } }); avalon.scan(); diff --git a/static/src/js/app/admin/contest/submissionList.js b/static/src/js/app/admin/contest/submissionList.js deleted file mode 100644 index 34fd5914..00000000 --- a/static/src/js/app/admin/contest/submissionList.js +++ /dev/null @@ -1,88 +0,0 @@ -require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) { - - avalon.ready(function () { - - if (avalon.vmodels.contestSubmissionList){ - var vm = avalon.vmodels.contestSubmissionList; - } - else { - - var vm = avalon.define({ - $id: "contestSubmissionList", - submissionList: [], - previousPage: 0, - nextPage: 0, - page: 1, - totalPage: 1, - results : { - 0: "Accepted", - 1: "Runtime Error", - 2: "Time Limit Exceeded", - 3: "Memory Limit Exceeded", - 4: "Compile Error", - 5: "Format Error", - 6: "Wrong Answer", - 7: "System Error", - 8: "Waiting" - }, - getNext: function () { - if (!vm.nextPage) - return; - getPageData(vm.page + 1); - }, - getPrevious: function () { - if (!vm.previousPage) - return; - getPageData(vm.page - 1); - }, - getBtnClass: function (btn) { - if (btn == "next") { - return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - else { - return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled"; - } - }, - getPage: function (page_index) { - if (!page_index) - var page_index = vm.page; - getPageData(page_index); - }, - showSubmissionDetailPage: function (submissionId) { - - }, - goBack: function(check){ - vm.$fire("up!showContestListPage"); - } - }); - } - - getPageData(1); - - function getPageData(page) { - var url = "/api/admin/contest_submission/?paging=true&page=" + page + "&page_size=10&contest_id=" + avalon.vmodels.admin.$contestId; - if (avalon.vmodels.admin.$problemId) - url += "&problem_id=" + avalon.vmodels.admin.$problemId - $.ajax({ - url: url, - dataType: "json", - method: "get", - success: function (data) { - if (!data.code) { - vm.submissionList = data.data.results; - vm.totalPage = data.data.total_page; - vm.previousPage = data.data.previous_page; - vm.nextPage = data.data.next_page; - vm.page = page; - } - else { - bsAlert(data.data); - } - } - }); - } - - - }); - avalon.scan(); -}); \ No newline at end of file diff --git a/static/src/js/app/admin/problem/addProblem.js b/static/src/js/app/admin/problem/addProblem.js index 61cb9401..dc19f0b5 100644 --- a/static/src/js/app/admin/problem/addProblem.js +++ b/static/src/js/app/admin/problem/addProblem.js @@ -135,17 +135,17 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE vm.testCaseId = response.data.test_case_id; vm.uploadSuccess = true; vm.testCaseList = []; - for (var i = 0; i < response.data.file_list.input.length; i++) { + for(var key in response.data.file_list){ vm.testCaseList.push({ - input: response.data.file_list.input[i], - output: response.data.file_list.output[i] - }); + input: response.data.file_list[key].input_name, + output: response.data.file_list[key].output_name + }) } bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); } }, function (file, percentage) { - vm.uploadProgress = percentage; + vm.uploadProgress = parseInt(percentage * 100); }); var tagAutoCompleteList = []; diff --git a/static/src/js/app/admin/problem/editProblem.js b/static/src/js/app/admin/problem/editProblem.js index 4b936e02..9fa96251 100644 --- a/static/src/js/app/admin/problem/editProblem.js +++ b/static/src/js/app/admin/problem/editProblem.js @@ -137,17 +137,17 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE vm.testCaseId = response.data.test_case_id; vm.uploadSuccess = true; vm.testCaseList = []; - for (var i = 0; i < response.data.file_list.input.length; i++) { + for(var key in response.data.file_list){ vm.testCaseList.push({ - input: response.data.file_list.input[i], - output: response.data.file_list.output[i] - }); + input: response.data.file_list[key].input_name, + output: response.data.file_list[key].output_name + }) } bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据"); } }, function (file, percentage) { - vm.uploadProgress = percentage; + vm.uploadProgress = parseInt(percentage * 100); } ); diff --git a/static/src/js/build.js b/static/src/js/build.js index 69049461..9d2a3129 100644 --- a/static/src/js/build.js +++ b/static/src/js/build.js @@ -62,17 +62,18 @@ admin_8_pack: "app/admin/admin", login_9_pack: "app/oj/account/login", addContest_10_pack: "app/admin/contest/addContest", - changePassword_11_pack: "app/oj/account/changePassword", - monitor_12_pack: "app/admin/monitor/monitor", - editProblem_13_pack: "app/admin/contest/editProblem", - joinGroupRequestList_14_pack: "app/admin/group/joinGroupRequestList", - group_15_pack: "app/oj/group/group", - editProblem_16_pack: "app/admin/problem/editProblem", - register_17_pack: "app/oj/account/register", - groupDetail_18_pack: "app/admin/group/groupDetail", - contestPassword_19_pack: "app/oj/contest/contestPassword", - group_20_pack: "app/admin/group/group", - submissionList_21_pack: "app/admin/contest/submissionList" + contestPassword_11_pack: "app/oj/contest/contestPassword", + changePassword_12_pack: "app/oj/account/changePassword", + monitor_13_pack: "app/admin/monitor/monitor", + editProblem_14_pack: "app/admin/contest/editProblem", + joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList", + group_16_pack: "app/oj/group/group", + contestProblemList_17_pack: "app/admin/contest/contestProblemList", + editProblem_18_pack: "app/admin/problem/editProblem", + register_19_pack: "app/oj/account/register", + groupDetail_20_pack: "app/admin/group/groupDetail", + editContest_21_pack: "app/admin/contest/editContest", + group_22_pack: "app/admin/group/group", }, shim: { avalon: { @@ -123,38 +124,41 @@ name: "addContest_10_pack" }, { - name: "changePassword_11_pack" + name: "contestPassword_11_pack" }, { - name: "monitor_12_pack" + name: "changePassword_12_pack" }, { - name: "editProblem_13_pack" + name: "monitor_13_pack" }, { - name: "joinGroupRequestList_14_pack" + name: "editProblem_14_pack" }, { - name: "group_15_pack" + name: "joinGroupRequestList_15_pack" }, { - name: "editProblem_16_pack" + name: "group_16_pack" }, { - name: "register_17_pack" + name: "contestProblemList_17_pack" }, { - name: "groupDetail_18_pack" + name: "editProblem_18_pack" }, { - name: "contestPassword_19_pack" + name: "register_19_pack" }, { - name: "group_20_pack" + name: "groupDetail_20_pack" }, { - name: "submissionList_21_pack" + name: "editContest_21_pack" }, + { + name: "group_22_pack" + } ], optimizeCss: "standard", }) \ No newline at end of file diff --git a/static/src/js/config.js b/static/src/js/config.js index 2e744a5b..f0b10473 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -64,17 +64,18 @@ var require = { admin_8_pack: "app/admin/admin", login_9_pack: "app/oj/account/login", addContest_10_pack: "app/admin/contest/addContest", - changePassword_11_pack: "app/oj/account/changePassword", - monitor_12_pack: "app/admin/monitor/monitor", - editProblem_13_pack: "app/admin/contest/editProblem", - joinGroupRequestList_14_pack: "app/admin/group/joinGroupRequestList", - group_15_pack: "app/oj/group/group", - editProblem_16_pack: "app/admin/problem/editProblem", - register_17_pack: "app/oj/account/register", - groupDetail_18_pack: "app/admin/group/groupDetail", - contestPassword_19_pack: "app/oj/contest/contestPassword", - group_20_pack: "app/admin/group/group", - submissionList_21_pack: "app/admin/contest/submissionList" + contestPassword_11_pack: "app/oj/contest/contestPassword", + changePassword_12_pack: "app/oj/account/changePassword", + monitor_13_pack: "app/admin/monitor/monitor", + editProblem_14_pack: "app/admin/contest/editProblem", + joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList", + group_16_pack: "app/oj/group/group", + contestProblemList_17_pack: "app/admin/contest/contestProblemList", + editProblem_18_pack: "app/admin/problem/editProblem", + register_19_pack: "app/oj/account/register", + groupDetail_20_pack: "app/admin/group/groupDetail", + editContest_21_pack: "app/admin/contest/editContest", + group_22_pack: "app/admin/group/group", }, shim: { avalon: { diff --git a/static/src/js/lib/avalon/avalon.js b/static/src/js/lib/avalon/avalon.js index 48c20211..296c2457 100644 --- a/static/src/js/lib/avalon/avalon.js +++ b/static/src/js/lib/avalon/avalon.js @@ -3,9 +3,9 @@ http://www.cnblogs.com/rubylouvre/ https://github.com/RubyLouvre http://weibo.com/jslouvre/ - + Released under the MIT license - avalon.shim.js 1.5.4 built in 2015.10.18 + avalon.shim.js 1.5.5 built in 2015.10.27 support IE6+ and other browsers ==================================================*/ (function(global, factory) { @@ -285,7 +285,7 @@ function _number(a, len) { //用于模拟slice, splice的效果 avalon.mix({ rword: rword, subscribers: subscribers, - version: 1.54, + version: 1.55, ui: {}, log: log, slice: W3C ? function (nodes, start, end) { @@ -940,6 +940,7 @@ kernel.maxRepeatSize = 100 avalon.config = kernel function $watch(expr, binding) { var $events = this.$events || (this.$events = {}) + var queue = $events[expr] || ($events[expr] = []) if (typeof binding === "function") { var backup = binding @@ -981,31 +982,41 @@ function $watch(expr, binding) { binding.element = DOC.createElement("a") } } - function $emit(key, args) { var event = this.$events if (event && event[key]) { if (args) { args[2] = key } - notifySubscribers(event[key], args) + var arr = event[key] + notifySubscribers(arr, args) var parent = this.$up if (parent) { if (this.$pathname) { $emit.call(parent, this.$pathname + "." + key, args)//以确切的值往上冒泡 } - $emit.call(parent, "*." + key, args)//以模糊的值往上冒泡 + $emit.call(parent, "*." + key, args)//以模糊的值往上冒泡 } } else { parent = this.$up + + if(this.$ups ){ + for(var i in this.$ups){ + $emit.call(this.$ups[i], i+"."+key, args)//以确切的值往上冒泡 + } + return + } if (parent) { - var path = this.$pathname + "." + key - var arr = path.split(".") + var p = this.$pathname + if (p === "") + p = "*" + var path = p + "." + key + arr = path.split(".") if (arr.indexOf("*") === -1) { $emit.call(parent, path, args)//以确切的值往上冒泡 arr[1] = "*" - $emit.call(parent, arr.join("."), args)//以确切的值往上冒泡 + $emit.call(parent, arr.join("."), args)//以模糊的值往上冒泡 } else { $emit.call(parent, path, args)//以确切的值往上冒泡 } @@ -1086,7 +1097,7 @@ avalon.define = function (source) { } //一些不需要被监听的属性 -var $$skipArray = oneObject("$id,$watch,$fire,$events,$model,$skipArray,$active,$pathname,$up,$track,$accessors") +var $$skipArray = oneObject("$id,$watch,$fire,$events,$model,$skipArray,$active,$pathname,$up,$track,$accessors,$ups") var defineProperty = Object.defineProperty var canHideOwn = true //如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 @@ -1212,6 +1223,7 @@ function observeObject(source, options) { }) /* jshint ignore:start */ + hideProperty($vmodel, "$ups", null) hideProperty($vmodel, "$id", "anonymous") hideProperty($vmodel, "$up", old ? old.$up : null) hideProperty($vmodel, "$track", Object.keys(hasOwn)) @@ -1236,15 +1248,15 @@ function observeObject(source, options) { }) } /* jshint ignore:end */ - //必须设置了$active,$events simple.forEach(function (name) { + var oldVal = old && old[name] var val = $vmodel[name] = source[name] if (val && typeof val === "object") { val.$up = $vmodel val.$pathname = name } - $emit.call($vmodel, name) + $emit.call($vmodel, name, [val,oldVal]) }) for (name in computed) { value = $vmodel[name] @@ -1316,7 +1328,7 @@ function observe(obj, old, hasReturn, watch) { if (Array.isArray(obj)) { return observeArray(obj, old, watch) } else if (avalon.isPlainObject(obj)) { - if (old) { + if (old && typeof old === 'object') { var keys = getKeys(obj) var keys2 = getKeys(old) if (keys.join(";") === keys2.join(";")) { @@ -1356,7 +1368,6 @@ function observeArray(array, old, watch) { for (var i in newProto) { array[i] = newProto[i] } - hideProperty(array, "$up", null) hideProperty(array, "$pathname", "") hideProperty(array, "$track", createTrack(array.length)) @@ -1489,7 +1500,7 @@ if (!canHideOwn) { //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 var uniq = {} - //添加访问器属性 + //添加访问器属性 for (name in accessors) { uniq[name] = true buffer.push( @@ -1593,7 +1604,7 @@ var newProto = { if (all.indexOf(this[i]) !== -1) { _splice.call(this.$track, i, 1) _splice.call(this, i, 1) - + } } } else if (typeof all === "function") { @@ -1602,7 +1613,7 @@ var newProto = { if (all(el, i)) { _splice.call(this.$track, i, 1) _splice.call(this, i, 1) - + } } } else { @@ -2517,7 +2528,7 @@ function showHidden(node, array) { var node = this[0] if (arguments.length === 0) { if (node.setTimeout) { //取得窗口尺寸 - return node["inner" + name] || + return node["inner" + name] || node.document.documentElement[clientProp] || node.document.body[clientProp] //IE6下前两个分别为undefined,0 } @@ -2862,7 +2873,7 @@ function parseExpr(expr, vmodels, binding) { } //======== -function stringifyExpr(code) { +function normalizeExpr(code) { var hasExpr = rexpr.test(code) //比如ms-class="width{{w}}"的情况 if (hasExpr) { var array = scanExpr(code) @@ -2877,6 +2888,7 @@ function stringifyExpr(code) { } } +avalon.normalizeExpr = normalizeExpr avalon.parseExprProxy = parseExpr var rthimRightParentheses = /\)\s*$/ @@ -2972,7 +2984,7 @@ function executeBindings(bindings, vmodels) { for (var i = 0, binding; binding = bindings[i++]; ) { binding.vmodels = vmodels directives[binding.type].init(binding) - + avalon.injectBinding(binding) if (binding.getter && binding.element.nodeType === 1) { //移除数据绑定,防止被二次解析 //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99 @@ -3230,7 +3242,7 @@ function scanTag(elem, vmodels, node) { avalon(elem).removeClass(name) createSignalTower(elem, newVmodel) } - + scanAttr(elem, vmodels) //扫描特性节点 } @@ -3409,7 +3421,7 @@ avalon.component = function (name, opts) { componentDefinition.$id = $id //==========构建VM========= - var keepSolt = componentDefinition.$slot + var keepSlot = componentDefinition.$slot var keepReplace = componentDefinition.$replace var keepContainer = componentDefinition.$container var keepTemplate = componentDefinition.$template @@ -3422,13 +3434,11 @@ avalon.component = function (name, opts) { elem.msResolved = 1 vmodel.$init(vmodel, elem) global.$init(vmodel, elem) - console.log("init") var nodes = elem.childNodes //收集插入点 var slots = {}, snode - for (var s = 0, el; el = nodes[s++]; ) { - var type = el.nodeType === 1 && el.getAttribute("slot") || keepSolt + var type = el.nodeType === 1 && el.getAttribute("slot") || keepSlot if (type) { if (slots[type]) { slots[type].push(el) @@ -3482,11 +3492,10 @@ avalon.component = function (name, opts) { e.stopPropagation() } } - console.log("dependencies "+dependencies) if (dependencies === 0) { var id1 = setTimeout(function () { clearTimeout(id1) - + vmodel.$ready(vmodel, elem, host.vmodels) global.$ready(vmodel, elem, host.vmodels) }, children ? Math.max(children * 17, 100) : 17) @@ -3512,7 +3521,6 @@ avalon.component = function (name, opts) { scanTag(elem, [vmodel].concat(host.vmodels)) avalon.vmodels[vmodel.$id] = vmodel - avalon.log("添加组件VM: "+vmodel.$id) if (!elem.childNodes.length) { avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) } else { @@ -3585,7 +3593,7 @@ function isWidget(el) { //如果为自定义标签,返回UI库的名字 if(el.scopeName && el.scopeName !== "HTML" ){ return el.scopeName } - var fullName = el.nodeName.toLowerCase() + var fullName = el.nodeName.toLowerCase() var index = fullName.indexOf(":") if (index > 0) { return fullName.slice(0, index) @@ -3627,7 +3635,7 @@ var attrDir = avalon.directive("attr", { init: function (binding) { //{{aaa}} --> aaa //{{aaa}}/bbb.html --> (aaa) + "/bbb.html" - binding.expr = stringifyExpr(binding.expr.trim()) + binding.expr = normalizeExpr(binding.expr.trim()) if (binding.type === "include") { var elem = binding.element effectBinding(elem, binding) @@ -3865,7 +3873,7 @@ var duplexBinding = avalon.directive("duplex", { } var updateVModel = function () { var val = elem.value //防止递归调用形成死循环 - if (composing || val === binding.oldValue) //处理中文输入法在minlengh下引发的BUG + if (composing || val === binding.oldValue || binding.pipe === null) //处理中文输入法在minlengh下引发的BUG return var lastValue = binding.pipe(val, binding, "get") try { @@ -3955,7 +3963,7 @@ var duplexBinding = avalon.directive("duplex", { var curValue = Array.isArray(value) ? value.map(String) : value + "" avalon(elem).val(curValue) elem.oldValue = curValue + "" - binding.changed.call(elem, curValue) + callback.call(elem, curValue) } }) break @@ -4000,15 +4008,18 @@ var duplexBinding = avalon.directive("duplex", { if (curValue !== this.oldValue) { var fixCaret = false if (elem.msFocus) { - var pos = getCaret(elem) - if (pos.start === pos.end) { - pos = pos.start - fixCaret = true + try { + var pos = getCaret(elem) + if (pos.start === pos.end) { + pos = pos.start + fixCaret = true + } + } catch (e) { } } elem.value = this.oldValue = curValue if (fixCaret) { - setCaret(element, pos, pos) + setCaret(elem, pos, pos) } } break @@ -4034,10 +4045,10 @@ var duplexBinding = avalon.directive("duplex", { case "select": //必须变成字符串后才能比较 binding._value = value - if(!elem.msHasEvent){ + if (!elem.msHasEvent) { elem.msHasEvent = "selectDuplex" //必须等到其孩子准备好才触发 - }else{ + } else { avalon.fireDom(elem, "datasetchanged", { bubble: elem.msHasEvent }) @@ -4045,7 +4056,7 @@ var duplexBinding = avalon.directive("duplex", { break } if (binding.xtype !== "select") { - binding.changed.call(elem, curValue) + binding.changed.call(elem, curValue, binding) } } }) @@ -4178,18 +4189,19 @@ function getCaret(ctrl, start, end) { function setCaret(ctrl, begin, end) { if (!ctrl.value || ctrl.readOnly) return - if (ctrl.setSelectionRange) { + if (ctrl.createTextRange) {//IE6-9 + setTimeout(function () { + var range = ctrl.createTextRange() + range.collapse(true); + range.moveStart("character", begin) + // range.moveEnd("character", end) #1125 + range.select() + }, 17) + } else { ctrl.selectionStart = begin ctrl.selectionEnd = end - } else { - var range = ctrl.createTextRange() - range.collapse(true); - range.moveStart("character", begin) - range.moveEnd("character", end - begin) - range.select() } } - avalon.directive("effect", { priority: 5, init: function (binding) { @@ -4209,7 +4221,7 @@ avalon.directive("effect", { if (!rexpr.test(text)) { className = quote(className) } else { - className = stringifyExpr(className) + className = normalizeExpr(className) } binding.expr = "[" + className + "," + rightExpr + "]" }, @@ -4372,7 +4384,7 @@ function upperFirstChar(str) { } var effectBuffer = new Buffer() function Effect() { -}// 动画实例,做成类的形式,是为了共用所有原型方法 +}// 动画实例,做成类的形式,是为了共用所有原型方法 Effect.prototype = { contrustor: Effect, @@ -4395,7 +4407,7 @@ Effect.prototype = { callEffectHook(me, "abort" + upperFirstChar(oppositeName)) callEffectHook(me, "before" + upperFirstChar(name)) if (!isLeave) - before(el) // 这里可能做插入DOM树的操作,因此必须在修改类名前执行 + before(el) // 这里可能做插入DOM树的操作,因此必须在修改类名前执行 var cssCallback = function (cancel) { el.removeEventListener(me.cssEvent, me.cssCallback) if (isLeave) { @@ -4622,7 +4634,7 @@ avalon.directive("if", { elem.required = false elem.setAttribute("_required", "true") } - try {// 如果不支持querySelectorAll或:required,可以直接无视 + try {// 如果不支持querySelectorAll或:required,可以直接无视 avalon.each(elem.querySelectorAll(":required"), function (el) { elem.required = false el.setAttribute("_required", "true") @@ -4725,7 +4737,7 @@ avalon.directive("include", { leaveEl.className = effectClass target.insertBefore(leaveEl, binding.start) // 插入到start之前,防止被错误的移动 } - + // cache or animate,移动节点 (templateCache || {})[lastID] = leaveEl var fragOnDom = binding.recoverNodes() // 恢复动画中的节点 @@ -4779,7 +4791,7 @@ avalon.directive("include", { return nodesToFrag(nodes) } } else { - before = function () {// 新添加元素的动画 + before = function () {// 新添加元素的动画 target.insertBefore(fragment, binding.end) scanNodeArray(nodes, vmodels) } @@ -4873,7 +4885,7 @@ var onDir = avalon.directive("on", { var fn = binding.getter || noop return fn.apply(this, binding.args.concat(e)) } - + var eventType = binding.param.replace(/-\d+$/, "") // ms-on-mousemove-10 if (eventType === "scan") { callback.call(elem, { @@ -4906,7 +4918,6 @@ avalon.directive("repeat", { effectBinding(elem, binding) binding.param = binding.param || "el" binding.sortedCallback = getBindingCallback(elem, "data-with-sorted", binding.vmodels) - // binding.renderedCallback = var rendered = getBindingCallback(elem, "data-" + type + "-rendered", binding.vmodels) var signature = generateID(type) @@ -4984,13 +4995,18 @@ avalon.directive("repeat", { var keyOrId = track[i] //array为随机数, object 为keyName var proxy = retain[keyOrId] if (!proxy) { + proxy = getProxyVM(this) - + proxy.$up = null if (xtype === "array") { action = "add" proxy.$id = keyOrId - - proxy[param] = value[i] //index + var valueItem = value[i] + proxy[param] = valueItem //index + if(Object(valueItem) === valueItem){ + valueItem.$ups = valueItem.$ups || {} + valueItem.$ups[param] = proxy + } } else { action = "append" @@ -5011,7 +5027,7 @@ avalon.directive("repeat", { } //重写proxy - if (this.enterCount === 1) {// 防止多次进入,导致位置不对 + if (this.enterCount === 1) {// 防止多次进入,导致位置不对 proxy.$active = false proxy.$oldIndex = proxy.$index proxy.$active = true @@ -5024,7 +5040,7 @@ avalon.directive("repeat", { proxy.$last = i === length - 1 // proxy[param] = value[i] } else { - proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况 + proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况 } proxies.push(proxy) } @@ -5075,7 +5091,7 @@ avalon.directive("repeat", { } else if (proxy.$index !== proxy.$oldIndex) { (function (proxy2, preElement) { staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { - var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了 + var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了 var inserted = avalon.slice(curNode.childNodes) parent.insertBefore(curNode, preElement.nextSibling) animateRepeat(inserted, 1, binding) @@ -5456,7 +5472,7 @@ var filters = avalon.filters = { truncate: function(str, length, truncation) { //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串 length = length || 30 - truncation = typeof truncation === "string" ? truncation : "..." + truncation = typeof truncation === "string" ? truncation : "..." return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str) }, $filter: function(val) { @@ -5472,7 +5488,7 @@ var filters = avalon.filters = { }, camelize: camelize, //https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet - // chrome + // chrome // chrome // IE67chrome // IE67chrome @@ -5493,7 +5509,7 @@ var filters = avalon.filters = { }) }, escape: function(str) { - //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < + //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < return String(str). replace(/&/g, '&'). replace(rsurrogate, function(value) { @@ -5535,7 +5551,7 @@ var filters = avalon.filters = { 'a': am/pm marker 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) format string can also be one of the following predefined localizable formats: - + 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) diff --git a/template/src/admin/contest/contest_list.html b/template/src/admin/contest/contest_list.html index 0a1eeb42..829f6b23 100644 --- a/template/src/admin/contest/contest_list.html +++ b/template/src/admin/contest/contest_list.html @@ -29,8 +29,8 @@