Commit 806c35b3 by cahrens

Warn if unsaved changes exist in assessment.

TNL-3870
parent 1aee957a
...@@ -8,17 +8,7 @@ ...@@ -8,17 +8,7 @@
</span> </span>
</h3> </h3>
{% block title %} {% include "openassessmentblock/staff_area/oa_staff_grade_learners_count.html" with staff_assessment_ungraded=staff_assessment_ungraded staff_assessment_in_progress=staff_assessment_in_progress %}
<span class="staff__grade__status">
<span class="staff__grade__value">
<span class="copy">
{% blocktrans with ungraded=staff_assessment_ungraded|stringformat:"s" in_progress=staff_assessment_in_progress|stringformat:"s" %}
{{ ungraded }} Available and {{ in_progress }} Checked Out
{% endblocktrans %}
</span>
</span>
</span>
{% endblock %}
</header> </header>
<div class="ui-staff__content__section staff__grade__content ui-toggle-visibility__content"> <div class="ui-staff__content__section staff__grade__content ui-toggle-visibility__content">
......
{% load i18n %} {% load i18n %}
{% spaceless %} {% spaceless %}
{% block body %}
<div class="staff__grade__form ui-toggle-visibility__content" data-submission-uuid="{{ submission.uuid }}"> <div class="staff__grade__form ui-toggle-visibility__content" data-submission-uuid="{{ submission.uuid }}">
<div class="wrapper--staff-assessment"> <div class="wrapper--staff-assessment">
<div> <div>
...@@ -53,6 +52,5 @@ ...@@ -53,6 +52,5 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %}
{% endspaceless %} {% endspaceless %}
{% load i18n %}
<span class="staff__grade__status">
<span class="staff__grade__value">
<span class="copy">
{% blocktrans with ungraded=staff_assessment_ungraded|stringformat:"s" in_progress=staff_assessment_in_progress|stringformat:"s" %}
{{ ungraded }} Available and {{ in_progress }} Checked Out
{% endblocktrans %}
</span>
</span>
</span>
{% load i18n %} {% load i18n %}
{% spaceless %} {% spaceless %}
{% block body %}
<div class="wrapper--staff-assessment"> <div class="wrapper--staff-assessment">
<div class="step__instruction"> <div class="step__instruction">
<p>{% trans "Override this learner's current grade using the problem's rubric." %}</p> <p>{% trans "Override this learner's current grade using the problem's rubric." %}</p>
...@@ -46,5 +45,4 @@ ...@@ -46,5 +45,4 @@
</ul> </ul>
</div> </div>
</div> </div>
{% endblock %}
{% endspaceless %} {% endspaceless %}
...@@ -169,14 +169,24 @@ class StaffAreaMixin(object): ...@@ -169,14 +169,24 @@ class StaffAreaMixin(object):
staff_assessment_required = "staff-assessment" in self.assessment_steps staff_assessment_required = "staff-assessment" in self.assessment_steps
context['staff_assessment_required'] = staff_assessment_required context['staff_assessment_required'] = staff_assessment_required
if staff_assessment_required: if staff_assessment_required:
grading_stats = staff_api.get_staff_grading_statistics( context.update(
student_item["course_id"], student_item["item_id"] self.get_staff_assessment_statistics_context(student_item["course_id"], student_item["item_id"])
) )
context['staff_assessment_ungraded'] = grading_stats['ungraded']
context['staff_assessment_in_progress'] = grading_stats['in-progress']
return path, context return path, context
@staticmethod
def get_staff_assessment_statistics_context(course_id, item_id):
"""
Returns a context with staff assessment "ungraded" and "in-progress" counts.
"""
grading_stats = staff_api.get_staff_grading_statistics(course_id, item_id)
return {
'staff_assessment_ungraded': grading_stats['ungraded'],
'staff_assessment_in_progress': grading_stats['in-progress']
}
@XBlock.json_handler @XBlock.json_handler
@require_global_admin("SCHEDULE_TRAINING") @require_global_admin("SCHEDULE_TRAINING")
def schedule_training(self, data, suffix=''): # pylint: disable=W0613 def schedule_training(self, data, suffix=''): # pylint: disable=W0613
...@@ -272,6 +282,27 @@ class StaffAreaMixin(object): ...@@ -272,6 +282,27 @@ class StaffAreaMixin(object):
except PeerAssessmentInternalError: except PeerAssessmentInternalError:
return self.render_error(self._(u"Error getting staff grade information.")) return self.render_error(self._(u"Error getting staff grade information."))
@XBlock.handler
@require_course_staff("STUDENT_GRADE")
def render_staff_grade_counts(self, data, suffix=''): # pylint: disable=W0613
"""
Renders a form to show the number of ungraded and checked out assessments.
Must be course staff to render this view.
"""
try:
student_item_dict = self.get_student_item_dict()
context = self.get_staff_assessment_statistics_context(
student_item_dict.get('course_id'), student_item_dict.get('item_id')
)
path = 'openassessmentblock/staff_area/oa_staff_grade_learners_count.html'
return self.render_assessment(path, context)
except PeerAssessmentInternalError:
return self.render_error(self._(u"Error getting staff grade ungraded and checked out counts."))
def get_student_submission_context(self, student_username, submission): def get_student_submission_context(self, student_username, submission):
""" """
Get a context dict for rendering a student submission and associated rubric (for staff grading). Get a context dict for rendering a student submission and associated rubric (for staff grading).
......
...@@ -723,8 +723,8 @@ ...@@ -723,8 +723,8 @@
"template": "openassessmentblock/staff_area/oa_staff_area.html", "template": "openassessmentblock/staff_area/oa_staff_area.html",
"context": { "context": {
"staff_assessment_required": true, "staff_assessment_required": true,
"staff_assessment_ungraded": 9, "staff_assessment_ungraded": 5,
"staff_assessment_in_progress": 0, "staff_assessment_in_progress": 2,
"status_counts": { "status_counts": {
"self": 1, "self": 1,
"peer": 2, "peer": 2,
...@@ -754,6 +754,22 @@ ...@@ -754,6 +754,22 @@
"output": "oa_staff_area_full_grading_2.html" "output": "oa_staff_area_full_grading_2.html"
}, },
{ {
"template": "openassessmentblock/staff_area/oa_staff_grade_learners_count.html",
"context": {
"staff_assessment_ungraded": 9,
"staff_assessment_in_progress": 3
},
"output": "oa_staff_grade_learners_count_1.html"
},
{
"template": "openassessmentblock/staff_area/oa_staff_grade_learners_count.html",
"context": {
"staff_assessment_ungraded": 9,
"staff_assessment_in_progress": 2
},
"output": "oa_staff_grade_learners_count_2.html"
},
{
"template": "openassessmentblock/staff_area/oa_student_info.html", "template": "openassessmentblock/staff_area/oa_student_info.html",
"context": { "context": {
"rubric_criteria": [ "rubric_criteria": [
......
if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singularText,pluralText,n){if(n>1){return pluralText}else{return singularText}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}if(typeof OpenAssessment.Server==="undefined"||!OpenAssessment.Server){OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};var jsonContentType="application/json; charset=utf-8";OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var view=this;var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},renderLatex:function(element){element.filter(".allow--latex").each(function(){MathJax.Hub.Queue(["Typeset",MathJax.Hub,this])})},renderContinuedPeer:function(){var view=this;var url=this.url("render_peer_assessment");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{continue_grading:true}}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(studentUsername,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:studentUsername},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},staffGradeForm:function(){var url=this.url("render_staff_grade_form");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The staff assessment form could not be loaded.")])})}).promise()},submit:function(submission){var url=this.url("submit");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){var success=data[0];if(success){var studentId=data[1];var attemptNum=data[2];defer.resolveWith(this,[studentId,attemptNum])}else{var errorNum=data[1];var errorMsg=data[2];defer.rejectWith(this,[errorNum,errorMsg])}}).fail(function(){defer.rejectWith(this,["AJAX",gettext("This response could not be submitted.")])})}).promise()},save:function(submission){var url=this.url("save_submission");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This response could not be saved.")])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback");var payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},submitAssessment:function(assessmentType,payload){var url=this.url(assessmentType);return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify(payload),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("peer_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},selfAssess:function(optionsSelected,criterionFeedback,overallFeedback){return this.submitAssessment("self_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback})},staffAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},trainingAssess:function(optionsSelected){var url=this.url("training_assess");var payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},scheduleTraining:function(){var url=this.url("schedule_training");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},rescheduleUnfinishedTasks:function(){var url=this.url("reschedule_unfinished_tasks");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(options){var url=this.url("update_editor_context");var payload=JSON.stringify({prompts:options.prompts,feedback_prompt:options.feedbackPrompt,feedback_default_text:options.feedback_default_text,title:options.title,submission_start:options.submissionStart,submission_due:options.submissionDue,criteria:options.criteria,assessments:options.assessments,editor_assessments_order:options.editorAssessmentsOrder,file_upload_type:options.fileUploadType,white_listed_file_types:options.fileTypeWhiteList,allow_latex:options.latexEnabled,leaderboard_show:options.leaderboardNum});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This problem could not be saved.")])})}).promise()},checkReleased:function(){var url=this.url("check_released");var payload='""';return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()},getUploadUrl:function(contentType,filename){var url=this.url("upload_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({contentType:contentType,filename:filename}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve upload url.")])})}).promise()},getDownloadUrl:function(){var url=this.url("download_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve download url.")])})}).promise()},cancelSubmission:function(submissionID,comments){var url=this.url("cancel_submission");var payload=JSON.stringify({submission_uuid:submissionID,comments:comments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()}}}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singularText,pluralText,n){if(n>1){return pluralText}else{return singularText}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}OpenAssessment.BaseView=function(runtime,element,server,data){this.runtime=runtime;this.element=element;this.server=server;this.fileUploader=new OpenAssessment.FileUploader;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this.fileUploader,this,data);this.trainingView=new OpenAssessment.StudentTrainingView(this.element,this.server,this);this.selfView=new OpenAssessment.SelfView(this.element,this.server,this);this.peerView=new OpenAssessment.PeerView(this.element,this.server,this);this.staffView=new OpenAssessment.StaffView(this.element,this.server,this);this.gradeView=new OpenAssessment.GradeView(this.element,this.server,this);this.leaderboardView=new OpenAssessment.LeaderboardView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffAreaView=new OpenAssessment.StaffAreaView(this.element,this.server,this)};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps",this.element),800,{offset:-50})}},setUpCollapseExpand:function(parentElement){parentElement.on("click",".ui-toggle-visibility__control",function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");sel.toggleClass("is--collapsed")})},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffAreaView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.staffView.load();this.selfView.load();this.gradeView.load();this.leaderboardView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,message){var element=this.element;var container=null;if(type==="save"){container=".response__submission__actions"}else if(type==="submit"||type==="peer"||type==="self"||type==="student-training"){container=".step__actions"}else if(type==="feedback_assess"){container=".submission__feedback__actions"}else if(type==="upload"){container="#upload__error"}if(container===null){if(message!==null){console.log(message)}}else{$(container+" .message__content",element).html("<p>"+(message?_.escape(message):"")+"</p>");$(container,element).toggleClass("has--error",message!==null)}},showLoadError:function(stepName,errorMessage){if(!errorMessage){errorMessage=gettext("Unable to load")}var $container=$("#openassessment__"+stepName);$container.toggleClass("has--error",true);$container.find(".step__status__value i").removeClass().addClass("icon fa fa-exclamation-triangle");$container.find(".step__status__value .copy").html(_.escape(errorMessage))}};function OpenAssessmentBlock(runtime,element,data){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server,data);view.load()}OpenAssessment.FileUploader=function(){this.upload=function(url,file){return $.Deferred(function(defer){$.ajax({url:url,type:"PUT",data:file,async:false,processData:false,contentType:file.type}).done(function(){Logger.log("openassessment.upload_file",{fileName:file.name,fileSize:file.size,fileType:file.type});defer.resolve()}).fail(function(data,textStatus){defer.rejectWith(this,[textStatus])})}).promise()}};OpenAssessment.GradeView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.GradeView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("grade").done(function(html){$("#openassessment__grade",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__grade",view.element));view.installHandlers()}).fail(function(errMsg){baseView.showLoadError("grade",errMsg)})},installHandlers:function(){var sel=$("#openassessment__grade",this.element);this.baseView.setUpCollapseExpand(sel);var view=this;sel.find(".feedback__submit").click(function(eventObject){eventObject.preventDefault();view.submitFeedbackOnAssessment()})},feedbackText:function(text){if(typeof text==="undefined"){return $("#feedback__remarks__value",this.element).val()}else{$("#feedback__remarks__value",this.element).val(text)}},feedbackOptions:function(options){var view=this;if(typeof options==="undefined"){return $.map($(".feedback__overall__value:checked",view.element),function(element){return $(element).val()})}else{$(".feedback__overall__value",this.element).prop("checked",false);$.each(options,function(index,opt){$("#feedback__overall__value--"+opt,view.element).prop("checked",true)})}},setHidden:function(selector,hidden){selector.toggleClass("is--hidden",hidden);selector.attr("aria-hidden",hidden?"true":"false")},isHidden:function(selector){return selector.hasClass("is--hidden")&&selector.attr("aria-hidden")==="true"},feedbackState:function(newState){var containerSel=$(".submission__feedback__content",this.element);var instructionsSel=containerSel.find(".submission__feedback__instructions");var fieldsSel=containerSel.find(".submission__feedback__fields");var actionsSel=containerSel.find(".submission__feedback__actions");var transitionSel=containerSel.find(".transition__status");var messageSel=containerSel.find(".message--complete");if(typeof newState==="undefined"){var isSubmitting=containerSel.hasClass("is--transitioning")&&containerSel.hasClass("is--submitting")&&!this.isHidden(transitionSel)&&this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var hasSubmitted=containerSel.hasClass("is--submitted")&&this.isHidden(transitionSel)&&!this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var isOpen=!containerSel.hasClass("is--submitted")&&!containerSel.hasClass("is--transitioning")&&!containerSel.hasClass("is--submitting")&&this.isHidden(transitionSel)&&this.isHidden(messageSel)&&!this.isHidden(instructionsSel)&&!this.isHidden(fieldsSel)&&!this.isHidden(actionsSel);if(isOpen){return"open"}else if(isSubmitting){return"submitting"}else if(hasSubmitted){return"submitted"}else{throw"Invalid feedback state"}}else{if(newState==="open"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,false);this.setHidden(fieldsSel,false);this.setHidden(actionsSel,false);this.setHidden(transitionSel,true);this.setHidden(messageSel,true)}else if(newState==="submitting"){containerSel.toggleClass("is--transitioning",true);containerSel.toggleClass("is--submitting",true);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,false);this.setHidden(messageSel,true)}else if(newState==="submitted"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",true);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,true);this.setHidden(messageSel,false)}}},submitFeedbackOnAssessment:function(){var view=this;var baseView=this.baseView;$(".feedback__submit",this.element).toggleClass("is--disabled",true);view.feedbackState("submitting");this.server.submitFeedbackOnAssessment(this.feedbackText(),this.feedbackOptions()).done(function(){view.feedbackState("submitted")}).fail(function(errMsg){baseView.toggleActionError("feedback_assess",errMsg)})}};OpenAssessment.LeaderboardView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.LeaderboardView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("leaderboard").done(function(html){$("#openassessment__leaderboard",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__leaderboard",view.element))}).fail(function(errMsg){baseView.showLoadError("leaderboard",errMsg)})}};OpenAssessment.MessageView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.MessageView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("message").done(function(html){$("#openassessment__message",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__message",view.element))}).fail(function(errMsg){baseView.showLoadError("message",errMsg)})}};OpenAssessment.PeerView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.PeerView.prototype={load:function(){var view=this;this.server.render("peer_assessment").done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(false)}).fail(function(){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;view.continueAssessmentEnabled(false);this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(true)}).fail(function(){view.baseView.showLoadError("peer-assessment");view.continueAssessmentEnabled(true)})},continueAssessmentEnabled:function(enabled){var button=$("#peer-assessment__continue__grading",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#peer-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled,view))}sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();if(!isContinuedAssessment){view.peerAssess()}else{view.continuedPeerAssess()}});sel.find("#peer-assessment__continue__grading").click(function(eventObject){eventObject.preventDefault();view.loadContinuedAssessment()})},peerSubmitEnabled:function(enabled){var button=$("#peer-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){baseView.loadAssessmentModules();baseView.scrollToTop()})},continuedPeerAssess:function(){var view=this;var gradeView=this.baseView.gradeView;var baseView=view.baseView;view.peerAssessRequest(function(){view.loadContinuedAssessment();gradeView.load();baseView.scrollToTop()})},peerAssessRequest:function(successFunction){var view=this;var uuid=$("#openassessment__peer-assessment").data("submission-uuid");view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback(),uuid).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})}};OpenAssessment.ResponseView=function(element,server,fileUploader,baseView,data){this.element=element;this.server=server;this.fileUploader=fileUploader;this.baseView=baseView;this.savedResponse=[];this.files=null;this.fileType=null;this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null;this.data=data;this.fileUploaded=false};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,MAX_FILE_SIZE:5242880,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__response",view.element));view.installHandlers();view.setAutoSaveEnabled(true)}).fail(function(){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;var uploadType="";if(sel.find(".submission__answer__display__file").length){uploadType=sel.find(".submission__answer__display__file").data("upload-type")}this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(){view.handleResponseChanged()};sel.find(".submission__answer__part__text__value").on("change keyup drop paste",handleChange);var handlePrepareUpload=function(eventData){view.prepareUpload(eventData.target.files,uploadType)};sel.find("input[type=file]").on("change",handlePrepareUpload);sel.find("#submission__preview__item").hide();sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()});sel.find("#submission__preview").click(function(eventObject){eventObject.preventDefault();var previewText=sel.find(".submission__answer__part__text__value").val();var previewContainer=sel.find("#preview_content");previewContainer.html(previewText.replace(/\r\n|\r|\n/g,"<br />"));sel.find("#submission__preview__item").show();MathJax.Hub.Queue(["Typeset",MathJax.Hub,previewContainer[0]])});sel.find("#file__upload").click(function(eventObject){eventObject.preventDefault();$(".submission__answer__display__file",view.element).removeClass("is--hidden");view.fileUpload()})},setAutoSaveEnabled:function(enabled){if(enabled){if(this.autoSaveTimerId===null){this.autoSaveTimerId=setInterval($.proxy(this.autoSave,this),this.AUTO_SAVE_POLL_INTERVAL)}}else{if(this.autoSaveTimerId!==null){clearInterval(this.autoSaveTimerId)}}},submitEnabled:function(enabled){var sel=$("#step--response__submit",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled);return enabled}},saveEnabled:function(enabled){var sel=$("#submission__save",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},previewEnabled:function(enabled){var sel=$("#submission__preview",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},saveStatus:function(msg){var sel=$("#response__save_status h3",this.element);if(typeof msg==="undefined"){return sel.text()}else{var label=gettext("Status of Your Response");sel.html('<span class="sr">'+_.escape(label)+":"+"</span>\n"+msg)}},unsavedWarningEnabled:function(enabled){if(typeof enabled==="undefined"){return window.onbeforeunload!==null}else{if(enabled){window.onbeforeunload=function(){return gettext("If you leave this page without saving or submitting your response, you'll lose any work you've done on the response.")}}else{window.onbeforeunload=null}}},response:function(texts){var sel=$(".submission__answer__part__text__value",this.element);if(typeof texts==="undefined"){return sel.map(function(){return $.trim($(this).val())}).get()}else{sel.map(function(index){$(this).val(texts[index])})}},responseChanged:function(){var savedResponse=this.savedResponse;return this.response().some(function(element,index){return element!==savedResponse[index]})},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isNotBlank=!this.response().every(function(element){return $.trim(element)===""});this.submitEnabled(isNotBlank);if(this.responseChanged()){this.saveEnabled(isNotBlank);this.previewEnabled(isNotBlank);this.saveStatus(gettext("This response has not been saved."));this.unsavedWarningEnabled(true)}this.lastChangeTime=Date.now()},save:function(){this.errorOnLastSave=false;this.saveStatus(gettext("Saving..."));this.baseView.toggleActionError("save",null);this.unsavedWarningEnabled(false);var view=this;var savedResponse=this.response();this.server.save(savedResponse).done(function(){view.savedResponse=savedResponse;var currentResponse=view.response();var currentResponseIsEmpty=currentResponse.every(function(element){return element===""});view.submitEnabled(!currentResponseIsEmpty);var currentResponseEqualsSaved=currentResponse.every(function(element,index){return element===savedResponse[index]});if(currentResponseEqualsSaved){view.saveEnabled(false);view.saveStatus(gettext("This response has been saved but not submitted."))}}).fail(function(errMsg){view.saveStatus(gettext("Error"));view.baseView.toggleActionError("save",errMsg);view.errorOnLastSave=true})},submit:function(){this.submitEnabled(false);var view=this;var baseView=this.baseView;var fileDefer=$.Deferred();if(view.files!==null&&!view.fileUploaded){var msg=gettext("Do you want to upload your file before submitting?");if(confirm(msg)){fileDefer=view.fileUpload()}else{view.submitEnabled(true);return}}else{fileDefer.resolve()}fileDefer.pipe(function(){return view.confirmSubmission().pipe(function(){var submission=view.response();baseView.toggleActionError("response",null);return view.server.submit(submission)})}).done($.proxy(view.moveToNextStep,view)).fail(function(errCode,errMsg){if(errCode==="ENOMULTI"){view.moveToNextStep()}else{if(errMsg){baseView.toggleActionError("submit",errMsg)}view.submitEnabled(true)}})},moveToNextStep:function(){this.load();this.baseView.loadAssessmentModules();this.unsavedWarningEnabled(false)},confirmSubmission:function(){var msg=gettext("You're about to submit your response for this assignment. After you submit this response, you can't change it or submit a new response.");return $.Deferred(function(defer){if(confirm(msg)){defer.resolve()}else{defer.reject()}})},prepareUpload:function(files,uploadType){this.files=null;this.fileType=files[0].type;var ext=files[0].name.split(".").pop().toLowerCase();if(files[0].size>this.MAX_FILE_SIZE){this.baseView.toggleActionError("upload",gettext("File size must be 5MB or less."))}else if(uploadType==="image"&&this.data.ALLOWED_IMAGE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG or GIF")}else if(uploadType==="pdf-and-image"&&this.data.ALLOWED_FILE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG, GIF or PDF")}else if(uploadType==="custom"&&this.data.FILE_TYPE_WHITE_LIST.indexOf(ext)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+this.data.FILE_TYPE_WHITE_LIST.join(", "))}else if(this.data.FILE_EXT_BLACK_LIST.indexOf(ext)!==-1){this.baseView.toggleActionError("upload",gettext("File type is not allowed."))}else{this.baseView.toggleActionError("upload",null);this.files=files}$("#file__upload").toggleClass("is--disabled",this.files===null)},fileUpload:function(){var view=this;var fileUpload=$("#file__upload");fileUpload.addClass("is--disabled");var handleError=function(errMsg){view.baseView.toggleActionError("upload",errMsg);fileUpload.removeClass("is--disabled")};return this.server.getUploadUrl(view.fileType,view.files[0].name).done(function(url){var file=view.files[0];view.fileUploader.upload(url,file).done(function(){view.fileUrl();view.baseView.toggleActionError("upload",null);view.fileUploaded=true}).fail(handleError)}).fail(handleError)},fileUrl:function(){var view=this;var file=$("#submission__answer__file",view.element);view.server.getDownloadUrl().done(function(url){if(file.prop("tagName")==="IMG"){file.attr("src",url)}else{file.attr("href",url)}return url})}};OpenAssessment.Rubric=function(element){this.element=element};OpenAssessment.Rubric.prototype={criterionFeedback:function(criterionFeedback){var selector="textarea.answer__value";var feedback={};$(selector,this.element).each(function(index,sel){if(typeof criterionFeedback!=="undefined"){$(sel).val(criterionFeedback[sel.name]);feedback[sel.name]=criterionFeedback[sel.name]}else{feedback[sel.name]=$(sel).val()}});return feedback},overallFeedback:function(overallFeedback){var selector=".assessment__rubric__question--feedback__value";if(typeof overallFeedback==="undefined"){return $(selector,this.element).val()}else{$(selector,this.element).val(overallFeedback)}},optionsSelected:function(optionsSelected){var selector="input[type=radio]";if(typeof optionsSelected==="undefined"){var options={};$(selector+":checked",this.element).each(function(index,sel){options[sel.name]=sel.value});return options}else{$(selector,this.element).prop("checked",false);$(selector,this.element).each(function(index,sel){if(optionsSelected.hasOwnProperty(sel.name)){if(sel.value===optionsSelected[sel.name]){$(sel).prop("checked",true)}}})}},canSubmitCallback:function(callback){var rubric=this;callback(rubric.canSubmit());$(this.element).on("change keyup drop paste",function(){callback(rubric.canSubmit())})},canSubmit:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var numAvailable=$(".field--radio.assessment__rubric__question.has--options",this.element).length;var completedRequiredComments=true;$("textarea[required]",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText===""){completedRequiredComments=false}});return numChecked===numAvailable&&completedRequiredComments},showCorrections:function(corrections){var selector="input[type=radio]";var hasErrors=false;$(selector,this.element).each(function(index,sel){var listItem=$(sel).parents(".assessment__rubric__question");if(corrections.hasOwnProperty(sel.name)){hasErrors=true;listItem.find(".message--incorrect").removeClass("is--hidden");listItem.find(".message--correct").addClass("is--hidden")}else{listItem.find(".message--correct").removeClass("is--hidden");listItem.find(".message--incorrect").addClass("is--hidden")}});return hasErrors}};OpenAssessment.SelfView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.SelfView.prototype={load:function(){var view=this;this.server.render("self_assessment").done(function(html){$("#openassessment__self-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__self-assessment",view.element));view.installHandlers()}).fail(function(){view.showLoadError("self-assessment")})},installHandlers:function(){var view=this;var sel=$("#openassessment__self-assessment",view.element);this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#self-assessment--001__assessment",this.element); if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singularText,pluralText,n){if(n>1){return pluralText}else{return singularText}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}if(typeof OpenAssessment.Server==="undefined"||!OpenAssessment.Server){OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};var jsonContentType="application/json; charset=utf-8";OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},render:function(component){var view=this;var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},renderLatex:function(element){element.filter(".allow--latex").each(function(){MathJax.Hub.Queue(["Typeset",MathJax.Hub,this])})},renderContinuedPeer:function(){var view=this;var url=this.url("render_peer_assessment");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{continue_grading:true}}).done(function(data){defer.resolveWith(view,[data])}).fail(function(){defer.rejectWith(view,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(studentUsername,options){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:_.extend({student_username:studentUsername},options)}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("This section could not be loaded.")])})}).promise()},staffGradeForm:function(){var url=this.url("render_staff_grade_form");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The staff assessment form could not be loaded.")])})}).promise()},staffGradeCounts:function(){var url=this.url("render_staff_grade_counts");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(){defer.rejectWith(this,[gettext("The display of ungraded and checked out responses could not be loaded.")])})}).promise()},submit:function(submission){var url=this.url("submit");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){var success=data[0];if(success){var studentId=data[1];var attemptNum=data[2];defer.resolveWith(this,[studentId,attemptNum])}else{var errorNum=data[1];var errorMsg=data[2];defer.rejectWith(this,[errorNum,errorMsg])}}).fail(function(){defer.rejectWith(this,["AJAX",gettext("This response could not be submitted.")])})}).promise()},save:function(submission){var url=this.url("save_submission");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This response could not be saved.")])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback");var payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},submitAssessment:function(assessmentType,payload){var url=this.url(assessmentType);return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify(payload),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("peer_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},selfAssess:function(optionsSelected,criterionFeedback,overallFeedback){return this.submitAssessment("self_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback})},staffAssess:function(optionsSelected,criterionFeedback,overallFeedback,submissionID){return this.submitAssessment("staff_assess",{options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback,submission_uuid:submissionID})},trainingAssess:function(optionsSelected){var url=this.url("training_assess");var payload=JSON.stringify({options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},scheduleTraining:function(){var url=this.url("schedule_training");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This assessment could not be submitted.")])})})},rescheduleUnfinishedTasks:function(){var url=this.url("reschedule_unfinished_tasks");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""',contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(options){var url=this.url("update_editor_context");var payload=JSON.stringify({prompts:options.prompts,feedback_prompt:options.feedbackPrompt,feedback_default_text:options.feedback_default_text,title:options.title,submission_start:options.submissionStart,submission_due:options.submissionDue,criteria:options.criteria,assessments:options.assessments,editor_assessments_order:options.editorAssessmentsOrder,file_upload_type:options.fileUploadType,white_listed_file_types:options.fileTypeWhiteList,allow_latex:options.latexEnabled,leaderboard_show:options.leaderboardNum});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("This problem could not be saved.")])})}).promise()},checkReleased:function(){var url=this.url("check_released");var payload='""';return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()},getUploadUrl:function(contentType,filename){var url=this.url("upload_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({contentType:contentType,filename:filename}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve upload url.")])})}).promise()},getDownloadUrl:function(){var url=this.url("download_url");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({}),contentType:jsonContentType}).done(function(data){if(data.success){defer.resolve(data.url)}else{defer.rejectWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("Could not retrieve download url.")])})}).promise()},cancelSubmission:function(submissionID,comments){var url=this.url("cancel_submission");var payload=JSON.stringify({submission_uuid:submissionID,comments:comments});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload,contentType:jsonContentType}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}}).fail(function(){defer.rejectWith(this,[gettext("The submission could not be removed from the grading pool.")])})}).promise()}}}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}if(typeof window.ngetgext==="undefined"){window.ngettext=function(singularText,pluralText,n){if(n>1){return pluralText}else{return singularText}}}if(typeof window.Logger==="undefined"){window.Logger={log:function(){}}}if(typeof window.MathJax==="undefined"){window.MathJax={Hub:{Typeset:function(){},Queue:function(){}}}}OpenAssessment.BaseView=function(runtime,element,server,data){this.runtime=runtime;this.element=element;this.server=server;this.fileUploader=new OpenAssessment.FileUploader;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this.fileUploader,this,data);this.trainingView=new OpenAssessment.StudentTrainingView(this.element,this.server,this);this.selfView=new OpenAssessment.SelfView(this.element,this.server,this);this.peerView=new OpenAssessment.PeerView(this.element,this.server,this);this.staffView=new OpenAssessment.StaffView(this.element,this.server,this);this.gradeView=new OpenAssessment.GradeView(this.element,this.server,this);this.leaderboardView=new OpenAssessment.LeaderboardView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffAreaView=new OpenAssessment.StaffAreaView(this.element,this.server,this)};if(typeof OpenAssessment.unsavedChanges==="undefined"||!OpenAssessment.unsavedChanges){OpenAssessment.unsavedChanges={}}OpenAssessment.clearUnsavedChanges=function(){OpenAssessment.unsavedChanges={};window.onbeforeunload=null};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps",this.element),800,{offset:-50})}},setUpCollapseExpand:function(parentElement){parentElement.on("click",".ui-toggle-visibility__control",function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");sel.toggleClass("is--collapsed")})},load:function(){this.responseView.load();this.loadAssessmentModules();this.staffAreaView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.staffView.load();this.selfView.load();this.gradeView.load();this.leaderboardView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,message){var element=this.element;var container=null;if(type==="save"){container=".response__submission__actions"}else if(type==="submit"||type==="peer"||type==="self"||type==="student-training"){container=".step__actions"}else if(type==="feedback_assess"){container=".submission__feedback__actions"}else if(type==="upload"){container="#upload__error"}if(container===null){if(message!==null){console.log(message)}}else{$(container+" .message__content",element).html("<p>"+(message?_.escape(message):"")+"</p>");$(container,element).toggleClass("has--error",message!==null)}},showLoadError:function(stepName,errorMessage){if(!errorMessage){errorMessage=gettext("Unable to load")}var $container=$("#openassessment__"+stepName);$container.toggleClass("has--error",true);$container.find(".step__status__value i").removeClass().addClass("icon fa fa-exclamation-triangle");$container.find(".step__status__value .copy").html(_.escape(errorMessage))},unsavedWarningEnabled:function(enabled,key,message){if(typeof enabled==="undefined"){return window.onbeforeunload!==null}else{var usageID=$(this.element).data("usage-id");if(enabled){if(typeof OpenAssessment.unsavedChanges[usageID]==="undefined"||!OpenAssessment.unsavedChanges[usageID]){OpenAssessment.unsavedChanges[usageID]={}}OpenAssessment.unsavedChanges[usageID][key]=message;window.onbeforeunload=function(){for(var xblockUsageID in OpenAssessment.unsavedChanges){if(OpenAssessment.unsavedChanges.hasOwnProperty(xblockUsageID)){for(var key in OpenAssessment.unsavedChanges[xblockUsageID]){if(OpenAssessment.unsavedChanges[xblockUsageID].hasOwnProperty(key)){return OpenAssessment.unsavedChanges[xblockUsageID][key]}}}}}}else{if(typeof OpenAssessment.unsavedChanges[usageID]!=="undefined"){delete OpenAssessment.unsavedChanges[usageID][key];if($.isEmptyObject(OpenAssessment.unsavedChanges[usageID])){delete OpenAssessment.unsavedChanges[usageID]}if($.isEmptyObject(OpenAssessment.unsavedChanges)){window.onbeforeunload=null}}}}}};function OpenAssessmentBlock(runtime,element,data){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server,data);view.load()}OpenAssessment.FileUploader=function(){this.upload=function(url,file){return $.Deferred(function(defer){$.ajax({url:url,type:"PUT",data:file,async:false,processData:false,contentType:file.type}).done(function(){Logger.log("openassessment.upload_file",{fileName:file.name,fileSize:file.size,fileType:file.type});defer.resolve()}).fail(function(data,textStatus){defer.rejectWith(this,[textStatus])})}).promise()}};OpenAssessment.GradeView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.GradeView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("grade").done(function(html){$("#openassessment__grade",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__grade",view.element));view.installHandlers()}).fail(function(errMsg){baseView.showLoadError("grade",errMsg)})},installHandlers:function(){var sel=$("#openassessment__grade",this.element);this.baseView.setUpCollapseExpand(sel);var view=this;sel.find(".feedback__submit").click(function(eventObject){eventObject.preventDefault();view.submitFeedbackOnAssessment()})},feedbackText:function(text){if(typeof text==="undefined"){return $("#feedback__remarks__value",this.element).val()}else{$("#feedback__remarks__value",this.element).val(text)}},feedbackOptions:function(options){var view=this;if(typeof options==="undefined"){return $.map($(".feedback__overall__value:checked",view.element),function(element){return $(element).val()})}else{$(".feedback__overall__value",this.element).prop("checked",false);$.each(options,function(index,opt){$("#feedback__overall__value--"+opt,view.element).prop("checked",true)})}},setHidden:function(selector,hidden){selector.toggleClass("is--hidden",hidden);selector.attr("aria-hidden",hidden?"true":"false")},isHidden:function(selector){return selector.hasClass("is--hidden")&&selector.attr("aria-hidden")==="true"},feedbackState:function(newState){var containerSel=$(".submission__feedback__content",this.element);var instructionsSel=containerSel.find(".submission__feedback__instructions");var fieldsSel=containerSel.find(".submission__feedback__fields");var actionsSel=containerSel.find(".submission__feedback__actions");var transitionSel=containerSel.find(".transition__status");var messageSel=containerSel.find(".message--complete");if(typeof newState==="undefined"){var isSubmitting=containerSel.hasClass("is--transitioning")&&containerSel.hasClass("is--submitting")&&!this.isHidden(transitionSel)&&this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var hasSubmitted=containerSel.hasClass("is--submitted")&&this.isHidden(transitionSel)&&!this.isHidden(messageSel)&&this.isHidden(instructionsSel)&&this.isHidden(fieldsSel)&&this.isHidden(actionsSel);var isOpen=!containerSel.hasClass("is--submitted")&&!containerSel.hasClass("is--transitioning")&&!containerSel.hasClass("is--submitting")&&this.isHidden(transitionSel)&&this.isHidden(messageSel)&&!this.isHidden(instructionsSel)&&!this.isHidden(fieldsSel)&&!this.isHidden(actionsSel);if(isOpen){return"open"}else if(isSubmitting){return"submitting"}else if(hasSubmitted){return"submitted"}else{throw"Invalid feedback state"}}else{if(newState==="open"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,false);this.setHidden(fieldsSel,false);this.setHidden(actionsSel,false);this.setHidden(transitionSel,true);this.setHidden(messageSel,true)}else if(newState==="submitting"){containerSel.toggleClass("is--transitioning",true);containerSel.toggleClass("is--submitting",true);containerSel.toggleClass("is--submitted",false);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,false);this.setHidden(messageSel,true)}else if(newState==="submitted"){containerSel.toggleClass("is--transitioning",false);containerSel.toggleClass("is--submitting",false);containerSel.toggleClass("is--submitted",true);this.setHidden(instructionsSel,true);this.setHidden(fieldsSel,true);this.setHidden(actionsSel,true);this.setHidden(transitionSel,true);this.setHidden(messageSel,false)}}},submitFeedbackOnAssessment:function(){var view=this;var baseView=this.baseView;$(".feedback__submit",this.element).toggleClass("is--disabled",true);view.feedbackState("submitting");this.server.submitFeedbackOnAssessment(this.feedbackText(),this.feedbackOptions()).done(function(){view.feedbackState("submitted")}).fail(function(errMsg){baseView.toggleActionError("feedback_assess",errMsg)})}};OpenAssessment.LeaderboardView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.LeaderboardView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("leaderboard").done(function(html){$("#openassessment__leaderboard",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__leaderboard",view.element))}).fail(function(errMsg){baseView.showLoadError("leaderboard",errMsg)})}};OpenAssessment.MessageView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.MessageView.prototype={load:function(){var view=this;var baseView=this.baseView;this.server.render("message").done(function(html){$("#openassessment__message",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__message",view.element))}).fail(function(errMsg){baseView.showLoadError("message",errMsg)})}};OpenAssessment.PeerView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.PeerView.prototype={UNSAVED_WARNING_KEY:"peer-assessment",load:function(){var view=this;this.server.render("peer_assessment").done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(false)}).fail(function(){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;view.continueAssessmentEnabled(false);this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__peer-assessment",view.element));view.installHandlers(true)}).fail(function(){view.baseView.showLoadError("peer-assessment");view.continueAssessmentEnabled(true)})},continueAssessmentEnabled:function(enabled){var button=$("#peer-assessment__continue__grading",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled)}},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#peer-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}else{this.rubric=null}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled,view));this.rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view))}sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();if(!isContinuedAssessment){view.peerAssess()}else{view.continuedPeerAssess()}});sel.find("#peer-assessment__continue__grading").click(function(eventObject){eventObject.preventDefault();view.loadContinuedAssessment()})},peerSubmitEnabled:function(enabled){var button=$("#peer-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},assessmentRubricChanges:function(changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without submitting your peer assessment, you will lose any work you have done."))}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){baseView.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);baseView.loadAssessmentModules();baseView.scrollToTop()})},continuedPeerAssess:function(){var view=this;var gradeView=this.baseView.gradeView;var baseView=view.baseView;view.peerAssessRequest(function(){baseView.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);view.loadContinuedAssessment();gradeView.load();baseView.scrollToTop()})},peerAssessRequest:function(successFunction){var view=this;var uuid=$("#openassessment__peer-assessment").data("submission-uuid");view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback(),uuid).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})}};OpenAssessment.ResponseView=function(element,server,fileUploader,baseView,data){this.element=element;this.server=server;this.fileUploader=fileUploader;this.baseView=baseView;this.savedResponse=[];this.files=null;this.fileType=null;this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null;this.data=data;this.fileUploaded=false};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,MAX_FILE_SIZE:5242880,UNSAVED_WARNING_KEY:"learner-response",load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__response",view.element));view.installHandlers();view.setAutoSaveEnabled(true)}).fail(function(){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;var uploadType="";if(sel.find(".submission__answer__display__file").length){uploadType=sel.find(".submission__answer__display__file").data("upload-type")}this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(){view.handleResponseChanged()};sel.find(".submission__answer__part__text__value").on("change keyup drop paste",handleChange);var handlePrepareUpload=function(eventData){view.prepareUpload(eventData.target.files,uploadType)};sel.find("input[type=file]").on("change",handlePrepareUpload);sel.find("#submission__preview__item").hide();sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()});sel.find("#submission__preview").click(function(eventObject){eventObject.preventDefault();var previewText=sel.find(".submission__answer__part__text__value").val();var previewContainer=sel.find("#preview_content");previewContainer.html(previewText.replace(/\r\n|\r|\n/g,"<br />"));sel.find("#submission__preview__item").show();MathJax.Hub.Queue(["Typeset",MathJax.Hub,previewContainer[0]])});sel.find("#file__upload").click(function(eventObject){eventObject.preventDefault();$(".submission__answer__display__file",view.element).removeClass("is--hidden");view.fileUpload()})},setAutoSaveEnabled:function(enabled){if(enabled){if(this.autoSaveTimerId===null){this.autoSaveTimerId=setInterval($.proxy(this.autoSave,this),this.AUTO_SAVE_POLL_INTERVAL)}}else{if(this.autoSaveTimerId!==null){clearInterval(this.autoSaveTimerId)}}},submitEnabled:function(enabled){var sel=$("#step--response__submit",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled);return enabled}},saveEnabled:function(enabled){var sel=$("#submission__save",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},previewEnabled:function(enabled){var sel=$("#submission__preview",this.element);if(typeof enabled==="undefined"){return!sel.hasClass("is--disabled")}else{sel.toggleClass("is--disabled",!enabled)}},saveStatus:function(msg){var sel=$("#response__save_status h3",this.element);if(typeof msg==="undefined"){return sel.text()}else{var label=gettext("Status of Your Response");sel.html('<span class="sr">'+_.escape(label)+":"+"</span>\n"+msg)}},response:function(texts){var sel=$(".submission__answer__part__text__value",this.element);if(typeof texts==="undefined"){return sel.map(function(){return $.trim($(this).val())}).get()}else{sel.map(function(index){$(this).val(texts[index])})}},responseChanged:function(){var savedResponse=this.savedResponse;return this.response().some(function(element,index){return element!==savedResponse[index]})},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isNotBlank=!this.response().every(function(element){return $.trim(element)===""});this.submitEnabled(isNotBlank);if(this.responseChanged()){this.saveEnabled(isNotBlank);this.previewEnabled(isNotBlank);this.saveStatus(gettext("This response has not been saved."));this.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without saving or submitting your response, you will lose any work you have done on the response."))}this.lastChangeTime=Date.now()},save:function(){this.errorOnLastSave=false;this.saveStatus(gettext("Saving..."));this.baseView.toggleActionError("save",null);this.baseView.unsavedWarningEnabled(false,this.UNSAVED_WARNING_KEY);var view=this;var savedResponse=this.response();this.server.save(savedResponse).done(function(){view.savedResponse=savedResponse;var currentResponse=view.response();var currentResponseIsEmpty=currentResponse.every(function(element){return element===""});view.submitEnabled(!currentResponseIsEmpty);var currentResponseEqualsSaved=currentResponse.every(function(element,index){return element===savedResponse[index]});if(currentResponseEqualsSaved){view.saveEnabled(false);view.saveStatus(gettext("This response has been saved but not submitted."))}}).fail(function(errMsg){view.saveStatus(gettext("Error"));view.baseView.toggleActionError("save",errMsg);view.errorOnLastSave=true})},submit:function(){this.submitEnabled(false);var view=this;var baseView=this.baseView;var fileDefer=$.Deferred();if(view.files!==null&&!view.fileUploaded){var msg=gettext("Do you want to upload your file before submitting?");if(confirm(msg)){fileDefer=view.fileUpload()}else{view.submitEnabled(true);return}}else{fileDefer.resolve()}fileDefer.pipe(function(){return view.confirmSubmission().pipe(function(){var submission=view.response();baseView.toggleActionError("response",null);return view.server.submit(submission)})}).done($.proxy(view.moveToNextStep,view)).fail(function(errCode,errMsg){if(errCode==="ENOMULTI"){view.moveToNextStep()}else{if(errMsg){baseView.toggleActionError("submit",errMsg)}view.submitEnabled(true)}})},moveToNextStep:function(){this.load();this.baseView.loadAssessmentModules();this.baseView.unsavedWarningEnabled(false,this.UNSAVED_WARNING_KEY)},confirmSubmission:function(){var msg=gettext("You're about to submit your response for this assignment. After you submit this response, you can't change it or submit a new response.");return $.Deferred(function(defer){if(confirm(msg)){defer.resolve()}else{defer.reject()}})},prepareUpload:function(files,uploadType){this.files=null;this.fileType=files[0].type;var ext=files[0].name.split(".").pop().toLowerCase();if(files[0].size>this.MAX_FILE_SIZE){this.baseView.toggleActionError("upload",gettext("File size must be 5MB or less."))}else if(uploadType==="image"&&this.data.ALLOWED_IMAGE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG or GIF")}else if(uploadType==="pdf-and-image"&&this.data.ALLOWED_FILE_MIME_TYPES.indexOf(this.fileType)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+"JPG, PNG, GIF or PDF")}else if(uploadType==="custom"&&this.data.FILE_TYPE_WHITE_LIST.indexOf(ext)===-1){this.baseView.toggleActionError("upload",gettext("You can upload files with these file types: ")+this.data.FILE_TYPE_WHITE_LIST.join(", "))}else if(this.data.FILE_EXT_BLACK_LIST.indexOf(ext)!==-1){this.baseView.toggleActionError("upload",gettext("File type is not allowed."))}else{this.baseView.toggleActionError("upload",null);this.files=files}$("#file__upload").toggleClass("is--disabled",this.files===null)},fileUpload:function(){var view=this;var fileUpload=$("#file__upload");fileUpload.addClass("is--disabled");var handleError=function(errMsg){view.baseView.toggleActionError("upload",errMsg);fileUpload.removeClass("is--disabled")};return this.server.getUploadUrl(view.fileType,view.files[0].name).done(function(url){var file=view.files[0];view.fileUploader.upload(url,file).done(function(){view.fileUrl();view.baseView.toggleActionError("upload",null);view.fileUploaded=true}).fail(handleError)}).fail(handleError)},fileUrl:function(){var view=this;var file=$("#submission__answer__file",view.element);view.server.getDownloadUrl().done(function(url){if(file.prop("tagName")==="IMG"){file.attr("src",url)}else{file.attr("href",url)}return url})}};OpenAssessment.Rubric=function(element){this.element=element};OpenAssessment.Rubric.prototype={criterionFeedback:function(criterionFeedback){var selector="textarea.answer__value";var feedback={};$(selector,this.element).each(function(index,sel){if(typeof criterionFeedback!=="undefined"){$(sel).val(criterionFeedback[sel.name]);feedback[sel.name]=criterionFeedback[sel.name]}else{feedback[sel.name]=$(sel).val()}});return feedback},overallFeedback:function(overallFeedback){var selector=".assessment__rubric__question--feedback__value";if(typeof overallFeedback==="undefined"){return $(selector,this.element).val()}else{$(selector,this.element).val(overallFeedback)}},optionsSelected:function(optionsSelected){var selector="input[type=radio]";if(typeof optionsSelected==="undefined"){var options={};$(selector+":checked",this.element).each(function(index,sel){options[sel.name]=sel.value
if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled,this))}sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.selfAssess()})},selfSubmitEnabled:function(enabled){var button=$("#self-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);this.server.selfAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback()).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};OpenAssessment.StaffView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffView.prototype={load:function(){var view=this;this.server.render("staff_assessment").done(function(html){$("#openassessment__staff-assessment",view.element).replaceWith(html)}).fail(function(){view.baseView.showLoadError("staff-assessment")})}};!function(OpenAssessment){"use strict";OpenAssessment.StaffAreaView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffAreaView.prototype={load:function(onSuccessCallback){var view=this;if($(".openassessment__staff-area",view.element).length>0){this.server.render("staff_area").done(function(html){$(".openassessment__staff-area",view.element).replaceWith(html);view.server.renderLatex($(".openassessment__staff-area",view.element));view.installHandlers();if(onSuccessCallback){onSuccessCallback()}}).fail(function(){view.baseView.showLoadError("staff_area")})}},loadStudentInfo:function(options){var view=this;var $manageLearnersTab=$(".openassessment__staff-tools",this.element);var $form=$manageLearnersTab.find(".openassessment_student_info_form");var studentUsername=$manageLearnersTab.find(".openassessment__student_username").val();var showFormError=function(errorMessage){$form.find(".form--error").text(errorMessage)};var deferred=$.Deferred();$(".openassessment__student-info",view.element).text("");if(studentUsername.trim()){this.server.studentInfo(studentUsername,options).done(function(html){showFormError("");$(".openassessment__student-info",view.element).replaceWith(html);$manageLearnersTab.on("click",".action--submit-cancel-submission",function(eventObject){eventObject.preventDefault();view.cancelSubmission($(this).data("submission-uuid"))});var handleChange=function(eventData){view.handleCommentChanged(eventData)};$manageLearnersTab.find(".cancel_submission_comments").on("change keyup drop paste",handleChange);var $rubric=$manageLearnersTab.find(".staff-assessment__assessment");if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view,$manageLearnersTab));$manageLearnersTab.find(".wrapper--staff-assessment .action--submit",view.element).click(function(eventObject){var target=$(eventObject.currentTarget),rootElement=target.closest(".openassessment__student-info"),submissionID=rootElement.data("submission-uuid");eventObject.preventDefault();view.submitStaffOverride(submissionID,rubric,$manageLearnersTab)})}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));deferred.reject()})}else{showFormError(gettext("You must provide a learner name."));deferred.reject()}return deferred.promise()},loadStaffGradeForm:function(){var view=this;var $staffGradeTab=$(".openassessment__staff-grading",this.element);var isCollapsed=$staffGradeTab.find(".staff__grade__control").hasClass("is--collapsed");var deferred=$.Deferred();var showFormError=function(errorMessage){$staffGradeTab.find(".staff__grade__form--error").text(errorMessage)};if(isCollapsed&&!this.staffGradeFormLoaded){this.staffGradeFormLoaded=true;this.server.staffGradeForm().done(function(html){showFormError("");$staffGradeTab.find(".staff__grade__form").replaceWith(html);var $rubric=$staffGradeTab.find(".staff-assessment__assessment");if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view,$staffGradeTab));$staffGradeTab.find(".wrapper--staff-assessment .action--submit").click(function(eventObject){var submissionID=$staffGradeTab.find(".staff__grade__form").data("submission-uuid");eventObject.preventDefault();view.submitStaffGrade(submissionID,rubric,$staffGradeTab,$(eventObject.currentTarget).hasClass("continue_grading--action"))})}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));view.staffGradeFormLoaded=false;deferred.reject()})}return deferred.promise()},installHandlers:function(){var view=this;var $staffArea=$(".openassessment__staff-area",this.element);var $staffTools=$(".openassessment__staff-tools",$staffArea);var $staffInfo=$(".openassessment__student-info",$staffArea);var $staffGradeTool=$(".openassessment__staff-grading",$staffArea);if($staffArea.length<=0){return}this.baseView.setUpCollapseExpand($staffTools,function(){});this.baseView.setUpCollapseExpand($staffInfo,function(){});this.baseView.setUpCollapseExpand($staffGradeTool,function(){});$staffArea.find(".ui-staff__button").click(function(eventObject){var $button=$(eventObject.currentTarget),panelClass=$button.data("panel"),$panel=$staffArea.find("."+panelClass).first();if($button.hasClass("is--active")){$button.removeClass("is--active");$panel.addClass("is--hidden")}else{$staffArea.find(".ui-staff__button").removeClass("is--active");$button.addClass("is--active");$staffArea.find(".wrapper--ui-staff").addClass("is--hidden");$panel.removeClass("is--hidden")}});$staffArea.find(".ui-staff_close_button").click(function(eventObject){var $button=$(eventObject.currentTarget),$panel=$button.closest(".wrapper--ui-staff");$staffArea.find(".ui-staff__button").removeClass("is--active");$panel.addClass("is--hidden")});$staffTools.find(".openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-username").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});$staffTools.find(".action--submit-unfinished-tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()});$staffGradeTool.find(".staff__grade__show-form").click(function(){view.loadStaffGradeForm()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$(".schedule_training_message",view.element).text(msg)}).fail(function(errMsg){$(".schedule_training_message",view.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$(".reschedule_unfinished_tasks_message",view.element).text(msg)}).fail(function(errMsg){$(".reschedule_unfinished_tasks_message",view.element).text(errMsg)})},cancelSubmission:function(submissionUUID){this.cancelSubmissionEnabled(false);var view=this;var comments=$(".cancel_submission_comments",this.element).val();this.server.cancelSubmission(submissionUUID,comments).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){$(".cancel-submission-error").html(_.escape(errorMessage))})},cancelSubmissionEnabled:function(enabled){var $cancelButton=$(".action--submit-cancel-submission",this.element);if(typeof enabled==="undefined"){return!$cancelButton.hasClass("is--disabled")}else{$cancelButton.toggleClass("is--disabled",!enabled)}},comment:function(text){var $submissionComments=$(".cancel_submission_comments",this.element);if(typeof text==="undefined"){return $submissionComments.val()}else{$submissionComments.val(text)}},handleCommentChanged:function(){var isBlank=$.trim(this.comment())!=="";this.cancelSubmissionEnabled(isBlank)},staffSubmitEnabled:function(scope,enabled){var button=scope.find(".wrapper--staff-assessment .action--submit");if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},submitStaffOverride:function(submissionID,rubric,scope){var view=this;var successCallback=function(){view.loadStudentInfo({expanded_view:"final-grade"})};this.callStaffAssess(submissionID,rubric,scope,successCallback,".staff-override-error")},submitStaffGrade:function(submissionID,rubric,scope,continueGrading){var view=this;var successCallback=function(){view.staffGradeFormLoaded=false;var showFullGradeTab=function(){$(".button-staff-grading").click();if(continueGrading){$(".staff__grade__show-form",view.element).click()}};view.load(showFullGradeTab)};this.callStaffAssess(submissionID,rubric,scope,successCallback,".staff-grade-error")},callStaffAssess:function(submissionID,rubric,scope,successCallback,errorSelector){var view=this;view.staffSubmitEnabled(scope,false);this.server.staffAssess(rubric.optionsSelected(),rubric.criterionFeedback(),rubric.overallFeedback(),submissionID).done(successCallback).fail(function(errorMessage){scope.find(errorSelector).html(_.escape(errorMessage));view.staffSubmitEnabled(scope,true)})}}}(OpenAssessment);OpenAssessment.StudentTrainingView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.StudentTrainingView.prototype={load:function(){var view=this;this.server.render("student_training").done(function(html){$("#openassessment__student-training",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__student-training",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("student-training")})},installHandlers:function(){var sel=$("#openassessment__student-training",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#student-training--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.assessButtonEnabled,this))}sel.find("#student-training--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.assess()})},assess:function(){this.assessButtonEnabled(false);var options={};if(this.rubric!==null){options=this.rubric.optionsSelected()}var view=this;var baseView=this.baseView;this.server.trainingAssess(options).done(function(corrections){var incorrect=$("#openassessment__student-training--incorrect",view.element);var instructions=$("#openassessment__student-training--instructions",view.element);if(!view.rubric.showCorrections(corrections)){view.load();baseView.loadAssessmentModules();incorrect.addClass("is--hidden");instructions.removeClass("is--hidden")}else{instructions.addClass("is--hidden");incorrect.removeClass("is--hidden")}baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("student-training",errMsg);view.assessButtonEnabled(true)})},assessButtonEnabled:function(isEnabled){var button=$("#student-training--001__assessment__submit",this.element);if(typeof isEnabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!isEnabled)}}}; });return options}else{$(selector,this.element).prop("checked",false);$(selector,this.element).each(function(index,sel){if(optionsSelected.hasOwnProperty(sel.name)){if(sel.value===optionsSelected[sel.name]){$(sel).prop("checked",true)}}})}},canSubmitCallback:function(callback){var rubric=this;callback(rubric.canSubmit());$(this.element).on("change keyup drop paste",function(){callback(rubric.canSubmit())})},canSubmit:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var numAvailable=$(".field--radio.assessment__rubric__question.has--options",this.element).length;var completedRequiredComments=true;$("textarea[required]",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText===""){completedRequiredComments=false}});return numChecked===numAvailable&&completedRequiredComments},changesExistCallback:function(callback){var rubric=this;callback(rubric.changesExist());$(this.element).on("change keyup drop paste",function(){callback(rubric.changesExist())})},changesExist:function(){var numChecked=$("input[type=radio]:checked",this.element).length;var textExists=false;$("textarea",this.element).each(function(){var trimmedText=$.trim($(this).val());if(trimmedText!==""){textExists=true}});return numChecked>0||textExists},showCorrections:function(corrections){var selector="input[type=radio]";var hasErrors=false;$(selector,this.element).each(function(index,sel){var listItem=$(sel).parents(".assessment__rubric__question");if(corrections.hasOwnProperty(sel.name)){hasErrors=true;listItem.find(".message--incorrect").removeClass("is--hidden");listItem.find(".message--correct").addClass("is--hidden")}else{listItem.find(".message--correct").removeClass("is--hidden");listItem.find(".message--incorrect").addClass("is--hidden")}});return hasErrors}};OpenAssessment.SelfView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.SelfView.prototype={UNSAVED_WARNING_KEY:"self-assessment",load:function(){var view=this;this.server.render("self_assessment").done(function(html){$("#openassessment__self-assessment",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__self-assessment",view.element));view.installHandlers()}).fail(function(){view.showLoadError("self-assessment")})},installHandlers:function(){var view=this;var sel=$("#openassessment__self-assessment",view.element);this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#self-assessment--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}else{this.rubric=null}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled,this));this.rubric.changesExistCallback($.proxy(this.assessmentRubricChanges,this))}sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.selfAssess()})},selfSubmitEnabled:function(enabled){var button=$("#self-assessment--001__assessment__submit",this.element);if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},assessmentRubricChanges:function(changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,this.UNSAVED_WARNING_KEY,gettext("If you leave this page without submitting your self assessment, you will lose any work you have done."))}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);this.server.selfAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.rubric.overallFeedback()).done(function(){baseView.unsavedWarningEnabled(false,view.UNSAVED_WARNING_KEY);baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};OpenAssessment.StaffView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffView.prototype={load:function(){var view=this;this.server.render("staff_assessment").done(function(html){$("#openassessment__staff-assessment",view.element).replaceWith(html)}).fail(function(){view.baseView.showLoadError("staff-assessment")})}};!function(OpenAssessment){"use strict";OpenAssessment.StaffAreaView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffAreaView.prototype={FULL_GRADE_UNSAVED_WARNING_KEY:"staff-grade",OVERRIDE_UNSAVED_WARNING_KEY:"staff-override",load:function(){var view=this;if($(".openassessment__staff-area",view.element).length>0){this.server.render("staff_area").done(function(html){$(".openassessment__staff-area",view.element).replaceWith(html);view.server.renderLatex($(".openassessment__staff-area",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("staff_area")})}},loadStudentInfo:function(options){var view=this;var $manageLearnersTab=$(".openassessment__staff-tools",this.element);var $form=$manageLearnersTab.find(".openassessment_student_info_form");var studentUsername=$manageLearnersTab.find(".openassessment__student_username").val();var showFormError=function(errorMessage){$form.find(".form--error").text(errorMessage)};var deferred=$.Deferred();$(".openassessment__student-info",view.element).text("");if(studentUsername.trim()){this.server.studentInfo(studentUsername,options).done(function(html){showFormError("");$(".openassessment__student-info",view.element).replaceWith(html);$manageLearnersTab.on("click",".action--submit-cancel-submission",function(eventObject){eventObject.preventDefault();view.cancelSubmission($(this).data("submission-uuid"))});var handleChange=function(eventData){view.handleCommentChanged(eventData)};$manageLearnersTab.find(".cancel_submission_comments").on("change keyup drop paste",handleChange);var $rubric=$manageLearnersTab.find(".staff-assessment__assessment");if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view,$manageLearnersTab));rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view,view.OVERRIDE_UNSAVED_WARNING_KEY));$manageLearnersTab.find(".wrapper--staff-assessment .action--submit",view.element).click(function(eventObject){var target=$(eventObject.currentTarget),rootElement=target.closest(".openassessment__student-info"),submissionID=rootElement.data("submission-uuid");eventObject.preventDefault();view.submitStaffOverride(submissionID,rubric,$manageLearnersTab)})}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));deferred.reject()})}else{showFormError(gettext("You must provide a learner name."));deferred.reject()}return deferred.promise()},loadStaffGradeForm:function(clearAndCollapse){var view=this;var $staffGradeTab=$(".openassessment__staff-grading",this.element);var deferred=$.Deferred();var showFormError=function(errorMessage){$staffGradeTab.find(".staff__grade__form--error").text(errorMessage)};if(clearAndCollapse){$staffGradeTab.find(".staff__grade__control").toggleClass("is--collapsed",true);$staffGradeTab.find(".staff__grade__form").replaceWith('<div class="staff__grade__form"></div>');view.updateStaffGradeCounts();deferred.resolve()}else if(!this.staffGradeFormLoaded){this.staffGradeFormLoaded=true;this.server.staffGradeForm().done(function(html){showFormError("");$staffGradeTab.find(".staff__grade__form").replaceWith(html);view.updateStaffGradeCounts();var $rubric=$staffGradeTab.find(".staff-assessment__assessment");if($rubric.size()>0){var rubricElement=$rubric.get(0);var rubric=new OpenAssessment.Rubric(rubricElement);rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled,view,$staffGradeTab));rubric.changesExistCallback($.proxy(view.assessmentRubricChanges,view,view.FULL_GRADE_UNSAVED_WARNING_KEY));$staffGradeTab.find(".wrapper--staff-assessment .action--submit").click(function(eventObject){var submissionID=$staffGradeTab.find(".staff__grade__form").data("submission-uuid");eventObject.preventDefault();view.submitStaffGrade(submissionID,rubric,$staffGradeTab,$(eventObject.currentTarget).hasClass("continue_grading--action"))})}deferred.resolve()}).fail(function(){showFormError(gettext("Unexpected server error."));view.staffGradeFormLoaded=false;deferred.reject()})}return deferred.promise()},updateStaffGradeCounts:function(){var view=this;var $staffGradeTab=$(".openassessment__staff-grading",this.element);view.server.staffGradeCounts().done(function(html){$staffGradeTab.find(".staff__grade__status").replaceWith(html)}).fail(function(){$staffGradeTab.find(".staff__grade__status").replaceWith('<span class="staff__grade__status"><span class="staff__grade__value"><span class="copy">'+gettext("Error getting the number of ungraded responses")+"</span></span></span>")})},installHandlers:function(){var view=this;var $staffArea=$(".openassessment__staff-area",this.element);var $staffTools=$(".openassessment__staff-tools",$staffArea);var $staffInfo=$(".openassessment__student-info",$staffArea);var $staffGradeTool=$(".openassessment__staff-grading",$staffArea);if($staffArea.length<=0){return}this.baseView.setUpCollapseExpand($staffTools,function(){});this.baseView.setUpCollapseExpand($staffInfo,function(){});this.baseView.setUpCollapseExpand($staffGradeTool,function(){});$staffArea.find(".ui-staff__button").click(function(eventObject){var $button=$(eventObject.currentTarget),panelClass=$button.data("panel"),$panel=$staffArea.find("."+panelClass).first();if($button.hasClass("is--active")){$button.removeClass("is--active");$panel.addClass("is--hidden")}else{$staffArea.find(".ui-staff__button").removeClass("is--active");$button.addClass("is--active");$staffArea.find(".wrapper--ui-staff").addClass("is--hidden");$panel.removeClass("is--hidden")}});$staffArea.find(".ui-staff_close_button").click(function(eventObject){var $button=$(eventObject.currentTarget),$panel=$button.closest(".wrapper--ui-staff");$staffArea.find(".ui-staff__button").removeClass("is--active");$panel.addClass("is--hidden")});$staffTools.find(".openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-username").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});$staffTools.find(".action--submit-training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});$staffTools.find(".action--submit-unfinished-tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()});$staffGradeTool.find(".staff__grade__show-form").click(function(){var wasCollapsed=$staffGradeTool.find(".staff__grade__control").hasClass("is--collapsed");if(wasCollapsed){view.loadStaffGradeForm()}})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$(".schedule_training_message",view.element).text(msg)}).fail(function(errMsg){$(".schedule_training_message",view.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$(".reschedule_unfinished_tasks_message",view.element).text(msg)}).fail(function(errMsg){$(".reschedule_unfinished_tasks_message",view.element).text(errMsg)})},cancelSubmission:function(submissionUUID){this.cancelSubmissionEnabled(false);var view=this;var comments=$(".cancel_submission_comments",this.element).val();this.server.cancelSubmission(submissionUUID,comments).done(function(){view.loadStudentInfo({expanded_view:"final-grade"})}).fail(function(errorMessage){$(".cancel-submission-error").html(_.escape(errorMessage))})},cancelSubmissionEnabled:function(enabled){var $cancelButton=$(".action--submit-cancel-submission",this.element);if(typeof enabled==="undefined"){return!$cancelButton.hasClass("is--disabled")}else{$cancelButton.toggleClass("is--disabled",!enabled)}},comment:function(text){var $submissionComments=$(".cancel_submission_comments",this.element);if(typeof text==="undefined"){return $submissionComments.val()}else{$submissionComments.val(text)}},handleCommentChanged:function(){var isBlank=$.trim(this.comment())!=="";this.cancelSubmissionEnabled(isBlank)},staffSubmitEnabled:function(scope,enabled){var button=scope.find(".wrapper--staff-assessment .action--submit");if(typeof enabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!enabled);return enabled}},assessmentRubricChanges:function(key,changesExist){if(changesExist){this.baseView.unsavedWarningEnabled(true,key,gettext("If you leave this page without submitting your staff assessment, you will lose any work you have done."))}},submitStaffOverride:function(submissionID,rubric,scope){var view=this;var successCallback=function(){view.baseView.unsavedWarningEnabled(false,view.OVERRIDE_UNSAVED_WARNING_KEY);view.loadStudentInfo({expanded_view:"final-grade"})};this.callStaffAssess(submissionID,rubric,scope,successCallback,".staff-override-error")},submitStaffGrade:function(submissionID,rubric,scope,continueGrading){var view=this;var successCallback=function(){view.baseView.unsavedWarningEnabled(false,view.FULL_GRADE_UNSAVED_WARNING_KEY);view.staffGradeFormLoaded=false;view.loadStaffGradeForm(!continueGrading)};this.callStaffAssess(submissionID,rubric,scope,successCallback,".staff-grade-error")},callStaffAssess:function(submissionID,rubric,scope,successCallback,errorSelector){var view=this;view.staffSubmitEnabled(scope,false);this.server.staffAssess(rubric.optionsSelected(),rubric.criterionFeedback(),rubric.overallFeedback(),submissionID).done(successCallback).fail(function(errorMessage){scope.find(errorSelector).html(_.escape(errorMessage));view.staffSubmitEnabled(scope,true)})}}}(OpenAssessment);OpenAssessment.StudentTrainingView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.rubric=null};OpenAssessment.StudentTrainingView.prototype={load:function(){var view=this;this.server.render("student_training").done(function(html){$("#openassessment__student-training",view.element).replaceWith(html);view.server.renderLatex($("#openassessment__student-training",view.element));view.installHandlers()}).fail(function(){view.baseView.showLoadError("student-training")})},installHandlers:function(){var sel=$("#openassessment__student-training",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);var rubricSelector=$("#student-training--001__assessment",this.element);if(rubricSelector.size()>0){var rubricElement=rubricSelector.get(0);this.rubric=new OpenAssessment.Rubric(rubricElement)}if(this.rubric!==null){this.rubric.canSubmitCallback($.proxy(this.assessButtonEnabled,this))}sel.find("#student-training--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.assess()})},assess:function(){this.assessButtonEnabled(false);var options={};if(this.rubric!==null){options=this.rubric.optionsSelected()}var view=this;var baseView=this.baseView;this.server.trainingAssess(options).done(function(corrections){var incorrect=$("#openassessment__student-training--incorrect",view.element);var instructions=$("#openassessment__student-training--instructions",view.element);if(!view.rubric.showCorrections(corrections)){view.load();baseView.loadAssessmentModules();incorrect.addClass("is--hidden");instructions.removeClass("is--hidden")}else{instructions.addClass("is--hidden");incorrect.removeClass("is--hidden")}baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("student-training",errMsg);view.assessButtonEnabled(true)})},assessButtonEnabled:function(isEnabled){var button=$("#student-training--001__assessment__submit",this.element);if(typeof isEnabled==="undefined"){return!button.hasClass("is--disabled")}else{button.toggleClass("is--disabled",!isEnabled)}}};
\ No newline at end of file \ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -75,6 +75,10 @@ describe("OpenAssessment.PeerView", function() { ...@@ -75,6 +75,10 @@ describe("OpenAssessment.PeerView", function() {
server.renderLatex = jasmine.createSpy('renderLatex'); server.renderLatex = jasmine.createSpy('renderLatex');
}); });
afterEach(function() {
OpenAssessment.clearUnsavedChanges();
});
it("sends a peer assessment to the server", function() { it("sends a peer assessment to the server", function() {
var view = createPeerAssessmentView('oa_peer_assessment.html'); var view = createPeerAssessmentView('oa_peer_assessment.html');
submitPeerAssessment(view); submitPeerAssessment(view);
...@@ -115,4 +119,34 @@ describe("OpenAssessment.PeerView", function() { ...@@ -115,4 +119,34 @@ describe("OpenAssessment.PeerView", function() {
var view = createPeerAssessmentView('oa_turbo_mode.html'); var view = createPeerAssessmentView('oa_turbo_mode.html');
submitPeerAssessment(view); submitPeerAssessment(view);
}); });
it("warns of unsubmitted assessments", function() {
var view = createPeerAssessmentView('oa_peer_assessment.html');
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
// Click on radio buttons, to create unsubmitted changes.
$('.question__answers', view.el).each(function() {
$('input[type="radio"]', this).first().click();
});
expect(view.baseView.unsavedWarningEnabled()).toBe(true);
// When submitPeerAssessment is executed, the views will all re-render. However,
// as the test does not mock out the surrounding elements, the re-render
// of the peer assessment module will keep the original HTML intact (with selected
// options), causing the unsavedWarnings callback to be triggered again (after it is properly
// cleared during the submit operation). To avoid this, have the view re-render fail.
server.render = function() {
return $.Deferred(
function(defer) {
defer.fail();
}
).promise();
};
submitPeerAssessment(view);
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
});
}); });
...@@ -142,7 +142,7 @@ describe("OpenAssessment.ResponseView", function() { ...@@ -142,7 +142,7 @@ describe("OpenAssessment.ResponseView", function() {
view.setAutoSaveEnabled(false); view.setAutoSaveEnabled(false);
// Disable the unsaved page warning (if set) // Disable the unsaved page warning (if set)
view.unsavedWarningEnabled(false); OpenAssessment.clearUnsavedChanges();
}); });
it("updates and retrieves response text correctly", function() { it("updates and retrieves response text correctly", function() {
...@@ -299,36 +299,36 @@ describe("OpenAssessment.ResponseView", function() { ...@@ -299,36 +299,36 @@ describe("OpenAssessment.ResponseView", function() {
it("enables the unsaved work warning when the user changes the response text", function() { it("enables the unsaved work warning when the user changes the response text", function() {
// Initially, the unsaved work warning should be disabled // Initially, the unsaved work warning should be disabled
expect(view.unsavedWarningEnabled()).toBe(false); expect(view.baseView.unsavedWarningEnabled()).toBe(false);
// Change the text, then expect the unsaved warning to be enabled. // Change the text, then expect the unsaved warning to be enabled.
view.response(['Lorem ipsum 1', 'Lorem ipsum 2']); view.response(['Lorem ipsum 1', 'Lorem ipsum 2']);
view.handleResponseChanged(); view.handleResponseChanged();
// Expect the unsaved work warning to be enabled // Expect the unsaved work warning to be enabled
expect(view.unsavedWarningEnabled()).toBe(true); expect(view.baseView.unsavedWarningEnabled()).toBe(true);
}); });
it("disables the unsaved work warning when the user saves a response", function() { it("disables the unsaved work warning when the user saves a response", function() {
// Change the text, then expect the unsaved warning to be enabled. // Change the text, then expect the unsaved warning to be enabled.
view.response(['Lorem ipsum 1', 'Lorem ipsum 2']); view.response(['Lorem ipsum 1', 'Lorem ipsum 2']);
view.handleResponseChanged(); view.handleResponseChanged();
expect(view.unsavedWarningEnabled()).toBe(true); expect(view.baseView.unsavedWarningEnabled()).toBe(true);
// Save the response and expect the unsaved warning to be disabled // Save the response and expect the unsaved warning to be disabled
view.save(); view.save();
expect(view.unsavedWarningEnabled()).toBe(false); expect(view.baseView.unsavedWarningEnabled()).toBe(false);
}); });
it("disables the unsaved work warning when the user submits a response", function() { it("disables the unsaved work warning when the user submits a response", function() {
// Change the text, then expect the unsaved warning to be enabled. // Change the text, then expect the unsaved warning to be enabled.
view.response(['Lorem ipsum 1', 'Lorem ipsum 2']); view.response(['Lorem ipsum 1', 'Lorem ipsum 2']);
view.handleResponseChanged(); view.handleResponseChanged();
expect(view.unsavedWarningEnabled()).toBe(true); expect(view.baseView.unsavedWarningEnabled()).toBe(true);
// Submit the response and expect the unsaved warning to be disabled // Submit the response and expect the unsaved warning to be disabled
view.submit(); view.submit();
expect(view.unsavedWarningEnabled()).toBe(false); expect(view.baseView.unsavedWarningEnabled()).toBe(false);
}); });
describe("auto save", function() { describe("auto save", function() {
...@@ -360,7 +360,7 @@ describe("OpenAssessment.ResponseView", function() { ...@@ -360,7 +360,7 @@ describe("OpenAssessment.ResponseView", function() {
expect(view.saveStatus()).toContain('saved but not submitted'); expect(view.saveStatus()).toContain('saved but not submitted');
// Expect that the unsaved warning is disabled // Expect that the unsaved warning is disabled
expect(view.unsavedWarningEnabled()).toBe(false); expect(view.baseView.unsavedWarningEnabled()).toBe(false);
}); });
it("schedules autosave polling", function() { it("schedules autosave polling", function() {
......
...@@ -43,6 +43,10 @@ describe("OpenAssessment.SelfView", function() { ...@@ -43,6 +43,10 @@ describe("OpenAssessment.SelfView", function() {
view.installHandlers(); view.installHandlers();
}); });
afterEach(function() {
OpenAssessment.clearUnsavedChanges();
});
it("Sends a self assessment to the server", function() { it("Sends a self assessment to the server", function() {
spyOn(server, 'selfAssess').and.callThrough(); spyOn(server, 'selfAssess').and.callThrough();
...@@ -82,4 +86,33 @@ describe("OpenAssessment.SelfView", function() { ...@@ -82,4 +86,33 @@ describe("OpenAssessment.SelfView", function() {
// Expect the submit button to have been re-enabled // Expect the submit button to have been re-enabled
expect(view.selfSubmitEnabled()).toBe(true); expect(view.selfSubmitEnabled()).toBe(true);
}); });
it("warns of unsubmitted assessments", function() {
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
// Click on radio buttons, to create unsubmitted changes.
$('.question__answers', view.el).each(function() {
$('input[type="radio"]', this).first().click();
});
expect(view.baseView.unsavedWarningEnabled()).toBe(true);
// When selfAssess is executed, the views will all re-render. However,
// as the test does not mock out the surrounding elements, the re-render
// of the self assessment module will keep the original HTML intact (with selected
// options), causing the unsavedWarnings callback to be triggered again (after it is properly
// cleared during the submit operation). To avoid this, have the view re-render fail.
server.render = function() {
return $.Deferred(
function(defer) {
defer.fail();
}
).promise();
};
view.selfAssess();
expect(view.baseView.unsavedWarningEnabled()).toBe(false);
});
}); });
...@@ -21,6 +21,7 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -21,6 +21,7 @@ describe('OpenAssessment.StaffAreaView', function() {
this.studentTemplate = 'oa_student_info.html'; this.studentTemplate = 'oa_student_info.html';
this.staffAreaTemplate = 'oa_staff_area.html'; this.staffAreaTemplate = 'oa_staff_area.html';
this.staffGradeFormTemplate = 'oa_staff_grade_learners_assessment.html'; this.staffGradeFormTemplate = 'oa_staff_grade_learners_assessment.html';
this.staffGradeCountsTemplate = 'oa_staff_grade_learners_count_1.html';
// Remember which fragments have been loaded // Remember which fragments have been loaded
this.fragmentsLoaded = []; this.fragmentsLoaded = [];
...@@ -70,6 +71,10 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -70,6 +71,10 @@ describe('OpenAssessment.StaffAreaView', function() {
return this.mockLoadTemplate(server.staffGradeFormTemplate); return this.mockLoadTemplate(server.staffGradeFormTemplate);
}; };
this.staffGradeCounts = function() {
return this.mockLoadTemplate(server.staffGradeCountsTemplate);
};
this.data = {}; this.data = {};
}; };
...@@ -113,6 +118,33 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -113,6 +118,33 @@ describe('OpenAssessment.StaffAreaView', function() {
expect(server.fragmentsLoaded).toEqual(expectedFragments); expect(server.fragmentsLoaded).toEqual(expectedFragments);
}; };
var chooseStudent = function(view, studentName) {
var studentNameField = $('.openassessment__student_username', view.element),
submitButton = $('.action--submit-username', view.element);
studentNameField.val(studentName);
submitButton.click();
};
var showInstructorAssessmentForm = function(staffArea) {
$('.staff__grade__show-form', staffArea.element).click();
};
var fillAssessment = function($assessment) {
$('#staff__assessment__rubric__question--2__feedback', $assessment).val('Text response');
$('.question__answers', $assessment).each(function() {
$('input[type="radio"]', this).first().click();
});
};
var getAssessment = function(staffArea, tab) {
return $('.openassessment__' + tab + ' .wrapper--staff-assessment', staffArea.element);
};
var submitAssessment = function(staffArea, tab) {
var $submitButton = $('.action--submit', getAssessment(staffArea, tab));
$submitButton.click();
};
beforeEach(function() { beforeEach(function() {
// Create a new stub server // Create a new stub server
server = new StubServer(); server = new StubServer();
...@@ -133,6 +165,57 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -133,6 +165,57 @@ describe('OpenAssessment.StaffAreaView', function() {
}); });
}); });
describe('Detects unsaved changes', function () {
beforeEach(function() {
loadFixtures('oa_base_course_staff.html');
});
afterEach(function() {
// Disable the unsaved page warnings (if set).
OpenAssessment.clearUnsavedChanges();
});
it('tracks unsubmitted assessments in multiple views', function() {
var fullGradeStaffArea = createStaffArea({}, 'oa_staff_area_full_grading.html'),
staffOverrideStaffArea = createStaffArea(),
fullGradeTab = "staff-grading",
staffOverrideTab = "staff-tools",
$assessment;
expect(fullGradeStaffArea.baseView.unsavedWarningEnabled()).toBe(false);
expect(staffOverrideStaffArea.baseView.unsavedWarningEnabled()).toBe(false);
// Create unsubmitted changes in the "full grade" form.
showInstructorAssessmentForm(fullGradeStaffArea);
$assessment = getAssessment(fullGradeStaffArea, fullGradeTab);
fillAssessment($assessment);
expect(fullGradeStaffArea.baseView.unsavedWarningEnabled()).toBe(true);
expect(staffOverrideStaffArea.baseView.unsavedWarningEnabled()).toBe(true);
// Create unsubmitted changes in the "staff grade override" form.
chooseStudent(staffOverrideStaffArea, 'testStudent');
$assessment = getAssessment(staffOverrideStaffArea, staffOverrideTab);
fillAssessment($assessment);
expect(fullGradeStaffArea.baseView.unsavedWarningEnabled()).toBe(true);
expect(staffOverrideStaffArea.baseView.unsavedWarningEnabled()).toBe(true);
// Submit the full grade form.
submitAssessment(fullGradeStaffArea, fullGradeTab);
expect(fullGradeStaffArea.baseView.unsavedWarningEnabled()).toBe(true);
expect(staffOverrideStaffArea.baseView.unsavedWarningEnabled()).toBe(true);
// Submit the staff grade override form.
submitAssessment(staffOverrideStaffArea, staffOverrideTab);
expect(fullGradeStaffArea.baseView.unsavedWarningEnabled()).toBe(false);
expect(staffOverrideStaffArea.baseView.unsavedWarningEnabled()).toBe(false);
});
});
describe('AI training', function() { describe('AI training', function() {
it('schedules training of AI classifiers', function() { it('schedules training of AI classifiers', function() {
spyOn(server, 'scheduleTraining').and.callThrough(); spyOn(server, 'scheduleTraining').and.callThrough();
...@@ -255,12 +338,6 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -255,12 +338,6 @@ describe('OpenAssessment.StaffAreaView', function() {
}); });
describe('Manage Individual Learners', function() { describe('Manage Individual Learners', function() {
var chooseStudent = function(view, studentName) {
var studentNameField = $('.openassessment__student_username', view.element),
submitButton = $('.action--submit-username', view.element);
studentNameField.val(studentName);
submitButton.click();
};
beforeEach(function() { beforeEach(function() {
loadFixtures('oa_base_course_staff.html'); loadFixtures('oa_base_course_staff.html');
...@@ -357,27 +434,18 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -357,27 +434,18 @@ describe('OpenAssessment.StaffAreaView', function() {
}); });
describe('Staff Grade Override', function() { describe('Staff Grade Override', function() {
var fillAssessment = function($assessment) { var staffAreaTab = "staff-tools";
$('#staff__assessment__rubric__question--2__feedback', $assessment).val('Text response');
$('.question__answers', $assessment).each(function() {
$('input[type="radio"]', this).first().click();
});
};
var getAssessment = function(staffArea) { afterEach(function() {
return $('.openassessment__staff-tools .wrapper--staff-assessment', staffArea.element); // Disable the unsaved page warning (if set)
}; OpenAssessment.clearUnsavedChanges();
});
var submitAssessment = function(staffArea) {
var $submitButton = $('.action--submit', getAssessment(staffArea.element));
$submitButton.click();
};
it('enables the submit button when all required fields are specified', function() { it('enables the submit button when all required fields are specified', function() {
var staffArea = createStaffArea(), var staffArea = createStaffArea(),
$assessment, $submitButton; $assessment, $submitButton;
chooseStudent(staffArea, 'testStudent'); chooseStudent(staffArea, 'testStudent');
$assessment = getAssessment(staffArea.element); $assessment = getAssessment(staffArea, staffAreaTab);
$submitButton = $('.action--submit', $assessment); $submitButton = $('.action--submit', $assessment);
expect($submitButton).toHaveClass('is--disabled'); expect($submitButton).toHaveClass('is--disabled');
fillAssessment($assessment); fillAssessment($assessment);
...@@ -397,10 +465,10 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -397,10 +465,10 @@ describe('OpenAssessment.StaffAreaView', function() {
); );
// Fill in and submit the assessment // Fill in and submit the assessment
$assessment = getAssessment(staffArea.element); $assessment = getAssessment(staffArea, staffAreaTab);
fillAssessment($assessment); fillAssessment($assessment);
server.studentTemplate = 'oa_staff_graded_submission.html'; server.studentTemplate = 'oa_staff_graded_submission.html';
submitAssessment(staffArea); submitAssessment(staffArea, staffAreaTab);
// Verify that the student info is visible and shows the correct score // Verify that the student info is visible and shows the correct score
$gradeSection = $('.staff-info__student__grade', staffArea.element); $gradeSection = $('.staff-info__student__grade', staffArea.element);
...@@ -415,16 +483,36 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -415,16 +483,36 @@ describe('OpenAssessment.StaffAreaView', function() {
serverErrorMessage = 'Mock server error', serverErrorMessage = 'Mock server error',
$assessment; $assessment;
chooseStudent(staffArea, 'testStudent'); chooseStudent(staffArea, 'testStudent');
$assessment = getAssessment(staffArea.element); $assessment = getAssessment(staffArea, staffAreaTab);
fillAssessment($assessment); fillAssessment($assessment);
// Submit the assessment but return a server error message // Submit the assessment but return a server error message
server.staffAssess = failWith(server, serverErrorMessage); server.staffAssess = failWith(server, serverErrorMessage);
submitAssessment(staffArea); submitAssessment(staffArea, staffAreaTab);
// Verify that the error message is shown // Verify that the error message is shown
expect($('.staff-override-error', staffArea.element).first().text().trim()).toBe(serverErrorMessage); expect($('.staff-override-error', staffArea.element).first().text().trim()).toBe(serverErrorMessage);
}); });
it('warns of unsubmitted assessments', function() {
var staffArea = createStaffArea(),
$assessment;
chooseStudent(staffArea, 'testStudent');
expect(staffArea.baseView.unsavedWarningEnabled()).toBe(false);
// Fill in and submit the assessment
$assessment = getAssessment(staffArea, staffAreaTab);
fillAssessment($assessment);
expect(staffArea.baseView.unsavedWarningEnabled()).toBe(true);
server.studentTemplate = 'oa_staff_graded_submission.html';
submitAssessment(staffArea, staffAreaTab);
expect(staffArea.baseView.unsavedWarningEnabled()).toBe(false);
});
}); });
}); });
...@@ -447,35 +535,22 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -447,35 +535,22 @@ describe('OpenAssessment.StaffAreaView', function() {
}); });
describe('Grade Available Responses', function() { describe('Grade Available Responses', function() {
var showInstructorAssessmentForm = function(staffArea) { var staffAreaTab = "staff-grading";
$('.staff__grade__show-form', staffArea.element).click();
};
var fillAssessment = function($assessment) {
$('#staff__assessment__rubric__question--2__feedback', $assessment).val('Text response');
$('.question__answers', $assessment).each(function() {
$('input[type="radio"]', this).first().click();
});
};
var getAssessment = function(staffArea) {
return $('.openassessment__staff-grading .wrapper--staff-assessment', staffArea.element);
};
var submitAssessment = function(staffArea) {
var $submitButton = $('.action--submit', getAssessment(staffArea.element));
$submitButton.click();
};
beforeEach(function() { beforeEach(function() {
loadFixtures('oa_base_course_staff.html'); loadFixtures('oa_base_course_staff.html');
}); });
afterEach(function() {
// Disable the unsaved page warnings (if set).
OpenAssessment.clearUnsavedChanges();
});
it('enables both submit buttons when all required fields are specified', function() { it('enables both submit buttons when all required fields are specified', function() {
var staffArea = createStaffArea({}, 'oa_staff_area_full_grading.html'), var staffArea = createStaffArea({}, 'oa_staff_area_full_grading.html'),
$assessment, $submitButtons; $assessment, $submitButtons;
showInstructorAssessmentForm(staffArea); showInstructorAssessmentForm(staffArea);
$assessment = getAssessment(staffArea.element); $assessment = getAssessment(staffArea, staffAreaTab);
$submitButtons = $('.action--submit', $assessment); $submitButtons = $('.action--submit', $assessment);
expect($submitButtons.length).toBe(2); expect($submitButtons.length).toBe(2);
expect($submitButtons).toHaveClass('is--disabled'); expect($submitButtons).toHaveClass('is--disabled');
...@@ -487,7 +562,7 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -487,7 +562,7 @@ describe('OpenAssessment.StaffAreaView', function() {
var staffArea = createStaffArea({}, 'oa_staff_area_full_grading.html'), var staffArea = createStaffArea({}, 'oa_staff_area_full_grading.html'),
$assessment, $gradeSection; $assessment, $gradeSection;
showInstructorAssessmentForm(staffArea); showInstructorAssessmentForm(staffArea);
$assessment = getAssessment(staffArea.element); $assessment = getAssessment(staffArea, staffAreaTab);
// Verify that the submission is shown for the first user // Verify that the submission is shown for the first user
expect($('.staff-assessment__display__title', $assessment).text().trim()).toBe( expect($('.staff-assessment__display__title', $assessment).text().trim()).toBe(
...@@ -497,7 +572,7 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -497,7 +572,7 @@ describe('OpenAssessment.StaffAreaView', function() {
// Fill in and submit the assessment // Fill in and submit the assessment
fillAssessment($assessment); fillAssessment($assessment);
server.staffGradeFormTemplate = 'oa_staff_grade_learners_assessment_2.html'; server.staffGradeFormTemplate = 'oa_staff_grade_learners_assessment_2.html';
submitAssessment(staffArea); submitAssessment(staffArea, staffAreaTab);
// Verify that the assessment form has been removed // Verify that the assessment form has been removed
expect($('.staff__grade__form', staffArea.element).html().trim()).toBe(''); expect($('.staff__grade__form', staffArea.element).html().trim()).toBe('');
...@@ -514,7 +589,7 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -514,7 +589,7 @@ describe('OpenAssessment.StaffAreaView', function() {
); );
// Fill in and click the button to submit and request another submission // Fill in and click the button to submit and request another submission
$assessment = getAssessment(staffArea.element); $assessment = getAssessment(staffArea, staffAreaTab);
fillAssessment($assessment); fillAssessment($assessment);
server.staffGradeFormTemplate = 'oa_staff_grade_learners_assessment_2.html'; server.staffGradeFormTemplate = 'oa_staff_grade_learners_assessment_2.html';
$('.continue_grading--action', $assessment).click(); $('.continue_grading--action', $assessment).click();
...@@ -540,12 +615,12 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -540,12 +615,12 @@ describe('OpenAssessment.StaffAreaView', function() {
serverErrorMessage = 'Mock server error', serverErrorMessage = 'Mock server error',
$assessment; $assessment;
showInstructorAssessmentForm(staffArea); showInstructorAssessmentForm(staffArea);
$assessment = getAssessment(staffArea.element); $assessment = getAssessment(staffArea, staffAreaTab);
fillAssessment($assessment); fillAssessment($assessment);
// Submit the assessment but return a server error message // Submit the assessment but return a server error message
server.staffAssess = failWith(server, serverErrorMessage); server.staffAssess = failWith(server, serverErrorMessage);
submitAssessment(staffArea); submitAssessment(staffArea, staffAreaTab);
// Verify that the error message is shown // Verify that the error message is shown
expect($('.staff-grade-error', staffArea.element).first().text().trim()).toBe(serverErrorMessage); expect($('.staff-grade-error', staffArea.element).first().text().trim()).toBe(serverErrorMessage);
...@@ -557,18 +632,38 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -557,18 +632,38 @@ describe('OpenAssessment.StaffAreaView', function() {
expect($('.staff__grade__value').text().trim()).toBe("10 Available and 2 Checked Out"); expect($('.staff__grade__value').text().trim()).toBe("10 Available and 2 Checked Out");
// Rendering the staff grading form will cause the counts to re-render as well.
// This will use the staffGradeCountsTemplate template, which mimics the count changes.
server.staffGradeCountsTemplate = 'oa_staff_grade_learners_count_1.html';
showInstructorAssessmentForm(staffArea); showInstructorAssessmentForm(staffArea);
// Render a different staff area teamplate the next time around so counts can update. expect($('.staff__grade__value').text().trim()).toBe("9 Available and 3 Checked Out");
server.staffAreaTemplate = 'oa_staff_area_full_grading_2.html';
// Fill in assessment and make sure the counts re-render. // Fill in assessment and make sure the code re-renders the count form.
$assessment = getAssessment(staffArea.element); $assessment = getAssessment(staffArea, staffAreaTab);
fillAssessment($assessment); fillAssessment($assessment);
server.staffGradeFormTemplate = 'oa_staff_grade_learners_assessment_2.html'; // Return a different counts template to mimic the counts changing again.
submitAssessment(staffArea); server.staffGradeCountsTemplate = 'oa_staff_grade_learners_count_2.html';
submitAssessment(staffArea, staffAreaTab);
expect($('.staff__grade__value').text().trim()).toBe("9 Available and 2 Checked Out");
});
it('warns of unsubmitted assessments', function() {
var staffArea = createStaffArea({}, 'oa_staff_area_full_grading.html'),
$assessment;
showInstructorAssessmentForm(staffArea);
expect(staffArea.baseView.unsavedWarningEnabled()).toBe(false);
// Fill in assessment and make sure the code re-renders the count form.
$assessment = getAssessment(staffArea, staffAreaTab);
fillAssessment($assessment);
expect(staffArea.baseView.unsavedWarningEnabled()).toBe(true);
expect($('.staff__grade__value').text().trim()).toBe("9 Available and 0 Checked Out"); submitAssessment(staffArea, staffAreaTab);
expect(staffArea.baseView.unsavedWarningEnabled()).toBe(false);
}); });
}); });
}); });
...@@ -28,6 +28,16 @@ OpenAssessment.BaseView = function(runtime, element, server, data) { ...@@ -28,6 +28,16 @@ OpenAssessment.BaseView = function(runtime, element, server, data) {
this.staffAreaView = new OpenAssessment.StaffAreaView(this.element, this.server, this); this.staffAreaView = new OpenAssessment.StaffAreaView(this.element, this.server, this);
}; };
if (typeof OpenAssessment.unsavedChanges === 'undefined' || !OpenAssessment.unsavedChanges) {
OpenAssessment.unsavedChanges = {};
}
// This is used by unit tests to reset state.
OpenAssessment.clearUnsavedChanges = function() {
OpenAssessment.unsavedChanges = {};
window.onbeforeunload = null;
};
OpenAssessment.BaseView.prototype = { OpenAssessment.BaseView.prototype = {
/** /**
...@@ -148,6 +158,55 @@ OpenAssessment.BaseView.prototype = { ...@@ -148,6 +158,55 @@ OpenAssessment.BaseView.prototype = {
$container.toggleClass('has--error', true); $container.toggleClass('has--error', true);
$container.find('.step__status__value i').removeClass().addClass('icon fa fa-exclamation-triangle'); $container.find('.step__status__value i').removeClass().addClass('icon fa fa-exclamation-triangle');
$container.find('.step__status__value .copy').html(_.escape(errorMessage)); $container.find('.step__status__value .copy').html(_.escape(errorMessage));
},
/**
* Enable/disable the "navigate away" warning to alert the user of unsaved changes.
*
* @param {boolean} enabled If specified, set whether the warning is enabled.
* @param {string} key A unique key related to the type of unsaved changes. Must be supplied
* if "enabled" is also supplied.
* @param {string} message The message to show if navigating away with unsaved changes. Only needed
* if "enabled" is true.
* @returns {boolean} Whether the warning is enabled (only if "enabled" argument is not supplied).
*/
unsavedWarningEnabled: function(enabled, key, message) {
if (typeof enabled === 'undefined') {
return (window.onbeforeunload !== null);
}
else {
// To support multiple ORA XBlocks on the same page, store state by XBlock usage-id.
var usageID = $(this.element).data("usage-id");
if (enabled) {
if (typeof OpenAssessment.unsavedChanges[usageID] === 'undefined' ||
!OpenAssessment.unsavedChanges[usageID]) {
OpenAssessment.unsavedChanges[usageID] = {};
}
OpenAssessment.unsavedChanges[usageID][key] = message;
window.onbeforeunload = function() {
for (var xblockUsageID in OpenAssessment.unsavedChanges) {
if (OpenAssessment.unsavedChanges.hasOwnProperty(xblockUsageID)) {
for (var key in OpenAssessment.unsavedChanges[xblockUsageID]) {
if (OpenAssessment.unsavedChanges[xblockUsageID].hasOwnProperty(key)) {
return OpenAssessment.unsavedChanges[xblockUsageID][key];
}
}
}
}
};
}
else {
if (typeof OpenAssessment.unsavedChanges[usageID] !== 'undefined') {
delete OpenAssessment.unsavedChanges[usageID][key];
if ($.isEmptyObject(OpenAssessment.unsavedChanges[usageID])) {
delete OpenAssessment.unsavedChanges[usageID];
}
if ($.isEmptyObject(OpenAssessment.unsavedChanges)) {
window.onbeforeunload = null;
}
}
}
}
} }
}; };
......
...@@ -18,6 +18,8 @@ OpenAssessment.PeerView = function(element, server, baseView) { ...@@ -18,6 +18,8 @@ OpenAssessment.PeerView = function(element, server, baseView) {
OpenAssessment.PeerView.prototype = { OpenAssessment.PeerView.prototype = {
UNSAVED_WARNING_KEY: "peer-assessment",
/** /**
Load the peer assessment view. Load the peer assessment view.
**/ **/
...@@ -101,10 +103,16 @@ OpenAssessment.PeerView.prototype = { ...@@ -101,10 +103,16 @@ OpenAssessment.PeerView.prototype = {
var rubricElement = rubricSelector.get(0); var rubricElement = rubricSelector.get(0);
this.rubric = new OpenAssessment.Rubric(rubricElement); this.rubric = new OpenAssessment.Rubric(rubricElement);
} }
else {
// If there was previously a rubric visible, clear the reference to it.
this.rubric = null;
}
// Install a change handler for rubric options to enable/disable the submit button // Install a change handler for rubric options to enable/disable the submit button
if (this.rubric !== null) { if (this.rubric !== null) {
this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled, view)); this.rubric.canSubmitCallback($.proxy(view.peerSubmitEnabled, view));
this.rubric.changesExistCallback($.proxy(view.assessmentRubricChanges, view));
} }
// Install a click handler for assessment // Install a click handler for assessment
...@@ -154,12 +162,29 @@ OpenAssessment.PeerView.prototype = { ...@@ -154,12 +162,29 @@ OpenAssessment.PeerView.prototype = {
}, },
/** /**
* Called when something is selected or typed in the assessment rubric.
* Used to set the unsaved changes warning dialog.
*
* @param {boolean} changesExist true if unsaved changes exist
*/
assessmentRubricChanges: function(changesExist) {
if (changesExist) {
this.baseView.unsavedWarningEnabled(
true,
this.UNSAVED_WARNING_KEY,
gettext("If you leave this page without submitting your peer assessment, you will lose any work you have done.") // jscs:ignore maximumLineLength
);
}
},
/**
Send an assessment to the server and update the view. Send an assessment to the server and update the view.
**/ **/
peerAssess: function() { peerAssess: function() {
var view = this; var view = this;
var baseView = view.baseView; var baseView = view.baseView;
this.peerAssessRequest(function() { this.peerAssessRequest(function() {
baseView.unsavedWarningEnabled(false, view.UNSAVED_WARNING_KEY);
baseView.loadAssessmentModules(); baseView.loadAssessmentModules();
baseView.scrollToTop(); baseView.scrollToTop();
}); });
...@@ -174,6 +199,7 @@ OpenAssessment.PeerView.prototype = { ...@@ -174,6 +199,7 @@ OpenAssessment.PeerView.prototype = {
var gradeView = this.baseView.gradeView; var gradeView = this.baseView.gradeView;
var baseView = view.baseView; var baseView = view.baseView;
view.peerAssessRequest(function() { view.peerAssessRequest(function() {
baseView.unsavedWarningEnabled(false, view.UNSAVED_WARNING_KEY);
view.loadContinuedAssessment(); view.loadContinuedAssessment();
gradeView.load(); gradeView.load();
baseView.scrollToTop(); baseView.scrollToTop();
......
...@@ -38,6 +38,8 @@ OpenAssessment.ResponseView.prototype = { ...@@ -38,6 +38,8 @@ OpenAssessment.ResponseView.prototype = {
// Maximum file size (5 MB) for an attached file. // Maximum file size (5 MB) for an attached file.
MAX_FILE_SIZE: 5242880, MAX_FILE_SIZE: 5242880,
UNSAVED_WARNING_KEY: "learner-response",
/** /**
Load the response (submission) view. Load the response (submission) view.
**/ **/
...@@ -235,37 +237,6 @@ OpenAssessment.ResponseView.prototype = { ...@@ -235,37 +237,6 @@ OpenAssessment.ResponseView.prototype = {
}, },
/** /**
Enable/disable the "navigate away" warning to alert the user of unsaved changes.
Args:
enabled (bool): If specified, set whether the warning is enabled.
Returns:
bool: Whether the warning is enabled.
Examples:
>> view.unsavedWarningEnabled(true); // enable the "unsaved" warning
>> view.unsavedWarningEnabled();
>> true
**/
unsavedWarningEnabled: function(enabled) {
if (typeof enabled === 'undefined') {
return (window.onbeforeunload !== null);
}
else {
if (enabled) {
window.onbeforeunload = function() {
// Keep this on one big line to avoid gettext bug: http://stackoverflow.com/a/24579117
return gettext("If you leave this page without saving or submitting your response, you'll lose any work you've done on the response."); // jscs:ignore maximumLineLength
};
}
else {
window.onbeforeunload = null;
}
}
},
/**
Set the response texts. Set the response texts.
Retrieve the response texts. Retrieve the response texts.
...@@ -339,7 +310,11 @@ OpenAssessment.ResponseView.prototype = { ...@@ -339,7 +310,11 @@ OpenAssessment.ResponseView.prototype = {
this.saveEnabled(isNotBlank); this.saveEnabled(isNotBlank);
this.previewEnabled(isNotBlank); this.previewEnabled(isNotBlank);
this.saveStatus(gettext('This response has not been saved.')); this.saveStatus(gettext('This response has not been saved.'));
this.unsavedWarningEnabled(true); this.baseView.unsavedWarningEnabled(
true,
this.UNSAVED_WARNING_KEY,
gettext("If you leave this page without saving or submitting your response, you will lose any work you have done on the response.") // jscs:ignore maximumLineLength
);
} }
// Record the current time (used for autosave) // Record the current time (used for autosave)
...@@ -360,7 +335,7 @@ OpenAssessment.ResponseView.prototype = { ...@@ -360,7 +335,7 @@ OpenAssessment.ResponseView.prototype = {
this.baseView.toggleActionError('save', null); this.baseView.toggleActionError('save', null);
// Disable the "unsaved changes" warning // Disable the "unsaved changes" warning
this.unsavedWarningEnabled(false); this.baseView.unsavedWarningEnabled(false, this.UNSAVED_WARNING_KEY);
var view = this; var view = this;
var savedResponse = this.response(); var savedResponse = this.response();
...@@ -463,7 +438,7 @@ OpenAssessment.ResponseView.prototype = { ...@@ -463,7 +438,7 @@ OpenAssessment.ResponseView.prototype = {
// Disable the "unsaved changes" warning if the user // Disable the "unsaved changes" warning if the user
// tries to navigate to another page. // tries to navigate to another page.
this.unsavedWarningEnabled(false); this.baseView.unsavedWarningEnabled(false, this.UNSAVED_WARNING_KEY);
}, },
/** /**
......
...@@ -156,6 +156,43 @@ OpenAssessment.Rubric.prototype = { ...@@ -156,6 +156,43 @@ OpenAssessment.Rubric.prototype = {
}, },
/** /**
* Install a callback handler to be notified when unsaved changes exist in a rubric form.
*
* @param {function} callback a function that accepts one argument, a boolean indicating
* whether the user has selected options or inserted text.
*/
changesExistCallback: function(callback) {
var rubric = this;
// Set the initial state
callback(rubric.changesExist());
// Install a handler to update on change
$(this.element).on('change keyup drop paste',
function() { callback(rubric.changesExist()); }
);
},
/**
* Helper method for determining of unsubmitted changes exist in the rubric.
*
* @returns {boolean} true if unsubmitted changes exist.
*/
changesExist: function() {
var numChecked = $('input[type=radio]:checked', this.element).length;
var textExists = false;
$('textarea', this.element).each(function() {
var trimmedText = $.trim($(this).val());
if (trimmedText !== "") {
textExists = true;
}
});
return (numChecked > 0 || textExists);
},
/**
Updates the rubric to display positive and negative messages on each Updates the rubric to display positive and negative messages on each
criterion. For each correction provided, the associated criterion will have criterion. For each correction provided, the associated criterion will have
an appropriate message displayed. an appropriate message displayed.
......
...@@ -18,6 +18,8 @@ OpenAssessment.SelfView = function(element, server, baseView) { ...@@ -18,6 +18,8 @@ OpenAssessment.SelfView = function(element, server, baseView) {
OpenAssessment.SelfView.prototype = { OpenAssessment.SelfView.prototype = {
UNSAVED_WARNING_KEY: "self-assessment",
/** /**
Load the self assessment view. Load the self assessment view.
**/ **/
...@@ -51,10 +53,16 @@ OpenAssessment.SelfView.prototype = { ...@@ -51,10 +53,16 @@ OpenAssessment.SelfView.prototype = {
var rubricElement = rubricSelector.get(0); var rubricElement = rubricSelector.get(0);
this.rubric = new OpenAssessment.Rubric(rubricElement); this.rubric = new OpenAssessment.Rubric(rubricElement);
} }
else {
// If there was previously a rubric visible, clear the reference to it.
this.rubric = null;
}
// Install a change handler for rubric options to enable/disable the submit button // Install a change handler for rubric options to enable/disable the submit button
if (this.rubric !== null) { if (this.rubric !== null) {
this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled, this)); this.rubric.canSubmitCallback($.proxy(this.selfSubmitEnabled, this));
this.rubric.changesExistCallback($.proxy(this.assessmentRubricChanges, this));
} }
// Install a click handler for the submit button // Install a click handler for the submit button
...@@ -95,6 +103,22 @@ OpenAssessment.SelfView.prototype = { ...@@ -95,6 +103,22 @@ OpenAssessment.SelfView.prototype = {
}, },
/** /**
* Called when something is selected or typed in the assessment rubric.
* Used to set the unsaved changes warning dialog.
*
* @param {boolean} changesExist true if unsaved changes exist
*/
assessmentRubricChanges: function(changesExist) {
if (changesExist) {
this.baseView.unsavedWarningEnabled(
true,
this.UNSAVED_WARNING_KEY,
gettext("If you leave this page without submitting your self assessment, you will lose any work you have done.") // jscs:ignore maximumLineLength
);
}
},
/**
Send a self-assessment to the server and update the view. Send a self-assessment to the server and update the view.
**/ **/
selfAssess: function() { selfAssess: function() {
...@@ -110,6 +134,7 @@ OpenAssessment.SelfView.prototype = { ...@@ -110,6 +134,7 @@ OpenAssessment.SelfView.prototype = {
this.rubric.overallFeedback() this.rubric.overallFeedback()
).done( ).done(
function() { function() {
baseView.unsavedWarningEnabled(false, view.UNSAVED_WARNING_KEY);
baseView.loadAssessmentModules(); baseView.loadAssessmentModules();
baseView.scrollToTop(); baseView.scrollToTop();
} }
......
...@@ -20,14 +20,13 @@ ...@@ -20,14 +20,13 @@
OpenAssessment.StaffAreaView.prototype = { OpenAssessment.StaffAreaView.prototype = {
FULL_GRADE_UNSAVED_WARNING_KEY: "staff-grade",
OVERRIDE_UNSAVED_WARNING_KEY: "staff-override",
/** /**
* Load the staff area. * Load the staff area.
*
* @param {function} onSuccessCallback an optional callback to be executed when the
* server successfully returns the staff area HTML. This callback will be the last thing
* executed, after rendering and installing click handlers.
*/ */
load: function(onSuccessCallback) { load: function() {
var view = this; var view = this;
// If we're course staff, the base template should contain a section // If we're course staff, the base template should contain a section
...@@ -39,9 +38,6 @@ ...@@ -39,9 +38,6 @@
$('.openassessment__staff-area', view.element).replaceWith(html); $('.openassessment__staff-area', view.element).replaceWith(html);
view.server.renderLatex($('.openassessment__staff-area', view.element)); view.server.renderLatex($('.openassessment__staff-area', view.element));
view.installHandlers(); view.installHandlers();
if (onSuccessCallback) {
onSuccessCallback();
}
}).fail(function() { }).fail(function() {
view.baseView.showLoadError('staff_area'); view.baseView.showLoadError('staff_area');
}); });
...@@ -97,6 +93,10 @@ ...@@ -97,6 +93,10 @@
// Install a change handler for rubric options to enable/disable the submit button // Install a change handler for rubric options to enable/disable the submit button
rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled, view, $manageLearnersTab)); rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled, view, $manageLearnersTab));
rubric.changesExistCallback(
$.proxy(view.assessmentRubricChanges, view, view.OVERRIDE_UNSAVED_WARNING_KEY)
);
// Install a click handler for the submit button // Install a click handler for the submit button
$manageLearnersTab.find('.wrapper--staff-assessment .action--submit', view.element).click( $manageLearnersTab.find('.wrapper--staff-assessment .action--submit', view.element).click(
function(eventObject) { function(eventObject) {
...@@ -125,19 +125,27 @@ ...@@ -125,19 +125,27 @@
* Upon request, loads the staff grade/assessment section of the staff area. * Upon request, loads the staff grade/assessment section of the staff area.
* This allows staff grading when staff assessment is a required step. * This allows staff grading when staff assessment is a required step.
* *
* @param {boolean} clearAndCollapse if true, clear the staff grade form and collapse it. Otherwise
* render the staff grade form if is not already loaded.
* @returns {promise} A promise representing the successful loading * @returns {promise} A promise representing the successful loading
* of the staff grade (assessment) section. * of the staff grade (assessment) section.
*/ */
loadStaffGradeForm: function() { loadStaffGradeForm: function(clearAndCollapse) {
var view = this; var view = this;
var $staffGradeTab = $('.openassessment__staff-grading', this.element); var $staffGradeTab = $('.openassessment__staff-grading', this.element);
var isCollapsed = $staffGradeTab.find('.staff__grade__control').hasClass("is--collapsed");
var deferred = $.Deferred(); var deferred = $.Deferred();
var showFormError = function(errorMessage) { var showFormError = function(errorMessage) {
$staffGradeTab.find('.staff__grade__form--error').text(errorMessage); $staffGradeTab.find('.staff__grade__form--error').text(errorMessage);
}; };
if (isCollapsed && !this.staffGradeFormLoaded) { if (clearAndCollapse) {
// Collapse the editor and update the counts.
$staffGradeTab.find('.staff__grade__control').toggleClass('is--collapsed', true);
$staffGradeTab.find('.staff__grade__form').replaceWith('<div class="staff__grade__form"></div>');
view.updateStaffGradeCounts();
deferred.resolve();
}
else if (!this.staffGradeFormLoaded) {
this.staffGradeFormLoaded = true; this.staffGradeFormLoaded = true;
this.server.staffGradeForm().done(function(html) { this.server.staffGradeForm().done(function(html) {
showFormError(''); showFormError('');
...@@ -145,6 +153,9 @@ ...@@ -145,6 +153,9 @@
// Load the HTML and install event handlers // Load the HTML and install event handlers
$staffGradeTab.find('.staff__grade__form').replaceWith(html); $staffGradeTab.find('.staff__grade__form').replaceWith(html);
// Update the number of ungraded and checked out assigments.
view.updateStaffGradeCounts();
var $rubric = $staffGradeTab.find('.staff-assessment__assessment'); var $rubric = $staffGradeTab.find('.staff-assessment__assessment');
if ($rubric.size() > 0) { if ($rubric.size() > 0) {
var rubricElement = $rubric.get(0); var rubricElement = $rubric.get(0);
...@@ -153,6 +164,10 @@ ...@@ -153,6 +164,10 @@
// Install a change handler for rubric options to enable/disable the submit button // Install a change handler for rubric options to enable/disable the submit button
rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled, view, $staffGradeTab)); rubric.canSubmitCallback($.proxy(view.staffSubmitEnabled, view, $staffGradeTab));
rubric.changesExistCallback(
$.proxy(view.assessmentRubricChanges, view, view.FULL_GRADE_UNSAVED_WARNING_KEY)
);
// Install a click handler for the submit buttons // Install a click handler for the submit buttons
$staffGradeTab.find('.wrapper--staff-assessment .action--submit').click( $staffGradeTab.find('.wrapper--staff-assessment .action--submit').click(
function(eventObject) { function(eventObject) {
...@@ -171,10 +186,29 @@ ...@@ -171,10 +186,29 @@
deferred.reject(); deferred.reject();
}); });
} }
return deferred.promise(); return deferred.promise();
}, },
/** /**
* Update the counts of ungraded and checked out assessments.
*/
updateStaffGradeCounts: function() {
var view = this;
var $staffGradeTab = $('.openassessment__staff-grading', this.element);
view.server.staffGradeCounts().done(function(html) {
$staffGradeTab.find('.staff__grade__status').replaceWith(html);
}).fail(function() {
$staffGradeTab.find('.staff__grade__status').replaceWith(
'<span class="staff__grade__status"><span class="staff__grade__value"><span class="copy">' +
gettext("Error getting the number of ungraded responses") +
'</span></span></span>'
);
});
},
/**
* Install event handlers for the view. * Install event handlers for the view.
*/ */
installHandlers: function() { installHandlers: function() {
...@@ -255,7 +289,10 @@ ...@@ -255,7 +289,10 @@
// Install a click handler for showing the staff grading form. // Install a click handler for showing the staff grading form.
$staffGradeTool.find('.staff__grade__show-form').click( $staffGradeTool.find('.staff__grade__show-form').click(
function() { function() {
view.loadStaffGradeForm(); var wasCollapsed = $staffGradeTool.find('.staff__grade__control').hasClass("is--collapsed");
if (wasCollapsed) {
view.loadStaffGradeForm();
}
} }
); );
}, },
...@@ -380,6 +417,23 @@ ...@@ -380,6 +417,23 @@
}, },
/** /**
* Called when something is selected or typed in the assessment rubric.
* Used to set the unsaved changes warning dialog.
*
* @param {string} key the unsaved changes key
* @param {boolean} changesExist true if unsaved changes exist
*/
assessmentRubricChanges: function(key, changesExist) {
if (changesExist) {
this.baseView.unsavedWarningEnabled(
true,
key,
gettext("If you leave this page without submitting your staff assessment, you will lose any work you have done.") // jscs:ignore maximumLineLength
);
}
},
/**
* Submit the staff assessment override. * Submit the staff assessment override.
* *
* @param {string} submissionID The ID of the submission to be submitted. * @param {string} submissionID The ID of the submission to be submitted.
...@@ -390,6 +444,7 @@ ...@@ -390,6 +444,7 @@
submitStaffOverride: function(submissionID, rubric, scope) { submitStaffOverride: function(submissionID, rubric, scope) {
var view = this; var view = this;
var successCallback = function() { var successCallback = function() {
view.baseView.unsavedWarningEnabled(false, view.OVERRIDE_UNSAVED_WARNING_KEY);
// Note: we ignore any message returned from the server and instead // Note: we ignore any message returned from the server and instead
// re-render the student info with the "Learner's Final Grade" // re-render the student info with the "Learner's Final Grade"
// section expanded. This section will show the learner's // section expanded. This section will show the learner's
...@@ -413,15 +468,9 @@ ...@@ -413,15 +468,9 @@
submitStaffGrade: function(submissionID, rubric, scope, continueGrading) { submitStaffGrade: function(submissionID, rubric, scope, continueGrading) {
var view = this; var view = this;
var successCallback = function() { var successCallback = function() {
view.baseView.unsavedWarningEnabled(false, view.FULL_GRADE_UNSAVED_WARNING_KEY);
view.staffGradeFormLoaded = false; view.staffGradeFormLoaded = false;
var showFullGradeTab = function() { view.loadStaffGradeForm(!continueGrading);
// Need to show the staff grade component again, unfortunately requiring a global selector.
$('.button-staff-grading').click();
if (continueGrading) {
$('.staff__grade__show-form', view.element).click();
}
};
view.load(showFullGradeTab);
}; };
this.callStaffAssess(submissionID, rubric, scope, successCallback, '.staff-grade-error'); this.callStaffAssess(submissionID, rubric, scope, successCallback, '.staff-grade-error');
}, },
......
...@@ -137,6 +137,29 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) { ...@@ -137,6 +137,29 @@ if (typeof OpenAssessment.Server === "undefined" || !OpenAssessment.Server) {
}, },
/** /**
* Renders the count of ungraded and checked out assessemtns.
*
* @returns {promise} A JQuery promise, which resolves with the HTML of the rendered section
* fails with an error message.
*/
staffGradeCounts: function() {
var url = this.url('render_staff_grade_counts');
return $.Deferred(function(defer) {
$.ajax({
url: url,
type: "POST",
dataType: "html"
}).done(function(data) {
defer.resolveWith(this, [data]);
}).fail(function() {
defer.rejectWith(
this, [gettext('The display of ungraded and checked out responses could not be loaded.')]
);
});
}).promise();
},
/**
* Send a submission to the XBlock. * Send a submission to the XBlock.
* *
* @param {string} submission The text of the student's submission. * @param {string} submission The text of the student's submission.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment