Commit 9896d39f by Will Daly

Merge pull request #203 from edx/will/saved-but-not-submitted

TIM-304
parents 94a82c6b 6a28f277
......@@ -71,7 +71,8 @@
<ul class="list list--actions">
<li class="list--actions__item">
<a aria-role="button" href="#" id="step--response__submit" class="action action--submit step--response__submit is--disabled">
<a aria-role="button" href="#" id="step--response__submit"
class="action action--submit step--response__submit {{ submit_enabled|yesno:",is--disabled" }}">
<span class="copy">Submit your response and move to the next step</span>
<i class="ico icon-caret-right"></i>
</a>
......
<div id="openassessment-base">
<ol>
<li id="openassessment__response" class="openassessment__steps__step step--response ui-toggle-visibility">
<div class="ui-toggle-visibility__content">
<div class="wrapper--step__content">
<div class="step__content">
<form id="response__submission" class="response__submission">
<ol class="list list--fields response__submission__content">
<li class="field field--textarea submission__answer" id="submission__answer">
<label class="sr" for="submission__answer__value">Provide your response to the question.</label>
<textarea id="submission__answer__value" placeholder=""></textarea>
<span class="tip">You may continue to work on your response until you submit it.</span>
</li>
</ol>
<div class="response__submission__actions">
<div class="message message--inline message--error message--error-server">
<h3 class="message__title">We could not save your progress</h3>
</div>
<ul class="list list--actions">
<li class="list--actions__item">
<button type="submit" id="submission__save" class="action action--save submission__save is--disabled">Save Your Progress</button>
<div id="response__save_status" class="response__submission__status">
<h3 class="response__submission__status__title">
<span class="sr">Your Working Submission Status:</span>
Unsaved draft
</h3>
</div>
</li>
</ul>
</div>
</form>
</div>
<div class="step__actions">
<div class="message message--inline message--error message--error-server">
<h3 class="message__title">We could not submit your response</h3>
</div>
<ul class="list list--actions">
<li class="list--actions__item">
<a aria-role="button" href="#" id="step--response__submit" class="action action--submit step--response__submit is--disabled">
<span class="copy">Submit your response and move to the next step</span>
<i class="ico icon-caret-right"></i>
</a>
</li>
</ul>
</div>
</div>
</div>
</li>
</ol>
</div>
if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}OpenAssessment.BaseUI=function(runtime,element,server){this.runtime=runtime;this.element=element;this.server=server};OpenAssessment.BaseUI.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps"),800,{offset:-50})}},setUpCollapseExpand:function(parentSel,onExpand){parentSel.find(".ui-toggle-visibility__control").click(function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");if(sel.hasClass("is--collapsed")&&onExpand!==undefined){onExpand()}sel.toggleClass("is--collapsed")})},load:function(){this.renderSubmissionStep();this.renderPeerAssessmentStep();this.renderSelfAssessmentStep();this.renderGradeStep()},renderSubmissionStep:function(){var ui=this;this.server.render("submission").done(function(html){$("#openassessment__response",ui.element).replaceWith(html);var sel=$("#openassessment__response",ui.element);ui.setUpCollapseExpand(sel);ui.responseChanged();sel.find("#submission__answer__value").keyup(function(eventData){ui.responseChanged()});sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();ui.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();ui.save()})}).fail(function(errMsg){ui.showLoadError("response")})},responseChanged:function(){var blankSubmission=$("#submission__answer__value",this.element).val()==="";$("#step--response__submit",this.element).toggleClass("is--disabled",blankSubmission);$("#submission__save",this.element).toggleClass("is--disabled",blankSubmission)},renderPeerAssessmentStep:function(){var ui=this;this.server.render("peer_assessment").done(function(html){$("#openassessment__peer-assessment",ui.element).replaceWith(html);var sel=$("#openassessment__peer-assessment",ui.element);ui.setUpCollapseExpand(sel,$.proxy(ui.renderContinuedPeerAssessmentStep,ui));sel.find("#peer-assessment--001__assessment").change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;$("#peer-assessment--001__assessment__submit",ui.element).toggleClass("is--disabled",numChecked!=numAvailable)});sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();ui.peerAssess()})}).fail(function(errMsg){ui.showLoadError("peer-assessment")})},renderContinuedPeerAssessmentStep:function(){var ui=this;this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",ui.element).replaceWith(html);var sel=$("#openassessment__peer-assessment",ui.element);ui.setUpCollapseExpand(sel);sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();ui.continuedPeerAssess()});sel.find("#peer-assessment--001__assessment").change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;$("#peer-assessment--001__assessment__submit",ui.element).toggleClass("is--disabled",numChecked!=numAvailable)})}).fail(function(errMsg){ui.showLoadError("peer-assessment")})},renderSelfAssessmentStep:function(){var ui=this;this.server.render("self_assessment").done(function(html){$("#openassessment__self-assessment",ui.element).replaceWith(html);var sel=$("#openassessment__self-assessment",ui.element);ui.setUpCollapseExpand(sel);$("#self-assessment--001__assessment",ui.element).change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;$("#self-assessment--001__assessment__submit",ui.element).toggleClass("is--disabled",numChecked!=numAvailable)});sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();ui.selfAssess()})}).fail(function(errMsg){ui.showLoadError("self-assessment")})},renderGradeStep:function(){var ui=this;this.server.render("grade").done(function(html){$("#openassessment__grade",ui.element).replaceWith(html);var sel=$("#openassessment__grade",ui.element);ui.setUpCollapseExpand(sel);sel.find("#feedback__submit").click(function(eventObject){eventObject.preventDefault();ui.submitFeedbackOnAssessment()})}).fail(function(errMsg){ui.showLoadError("grade",errMsg)})},save:function(){var ui=this;var submission=$("#submission__answer__value",ui.element).val();ui.setSaveStatus("Saving...");ui.toggleActionError("save",null);ui.server.save(submission).done(function(){ui.setSaveStatus("Saved but not submitted")}).fail(function(errMsg){ui.setSaveStatus("Error");ui.toggleActionError("save",errMsg)})},setSaveStatus:function(msg){$("#response__save_status h3",this.element).html(msg)},submit:function(){var ui=this;var submission=$("#submission__answer__value",ui.element).val();ui.toggleActionError("response",null);ui.server.submit(submission).done(function(studentId,attemptNum){ui.renderSubmissionStep();ui.renderPeerAssessmentStep()}).fail(function(errCode,errMsg){ui.toggleActionError("submit",errMsg)})},submitFeedbackOnAssessment:function(){var ui=this;var text=$("#feedback__remarks__value",ui.element).val();var options=$.map($(".feedback__overall__value:checked",ui.element),function(element,index){return $(element).val()});ui.server.submitFeedbackOnAssessment(text,options).done(function(){}).fail(function(errMsg){ui.toggleActionError("feedback_assess",errMsg)})},peerAssess:function(){var ui=this;ui.peerAssessRequest(function(){ui.renderPeerAssessmentStep();ui.renderSelfAssessmentStep();ui.renderGradeStep();ui.scrollToTop()})},continuedPeerAssess:function(){var ui=this;ui.peerAssessRequest(function(){ui.renderContinuedPeerAssessmentStep();ui.renderGradeStep()})},peerAssessRequest:function(successFunction){var submissionId=$("span#peer_submission_uuid",this.element)[0].innerHTML.trim();var optionsSelected={};$("#peer-assessment--001__assessment input[type=radio]:checked",this.element).each(function(index,sel){optionsSelected[sel.name]=sel.value});var feedback=$("#assessment__rubric__question--feedback__value",this.element).val();var ui=this;this.toggleActionError("peer",null);this.server.peerAssess(submissionId,optionsSelected,feedback).done(successFunction).fail(function(errMsg){ui.toggleActionError("peer",errMsg)})},selfAssess:function(){var submissionId=$("span#self_submission_uuid",this.element)[0].innerHTML.trim();var optionsSelected={};$("#self-assessment--001__assessment input[type=radio]:checked",this.element).each(function(index,sel){optionsSelected[sel.name]=sel.value});var ui=this;this.toggleActionError("self",null);this.server.selfAssess(submissionId,optionsSelected).done(function(){ui.renderPeerAssessmentStep();ui.renderSelfAssessmentStep();ui.renderGradeStep();ui.scrollToTop()}).fail(function(errMsg){ui.toggleActionError("self",errMsg)})},toggleActionError:function(type,msg){var container=null;if(type=="save"){container=".response__submission__actions"}else if(type=="submit"){container=".step__actions"}else if(type=="peer"){container=".step__actions"}else if(type=="self"){container=".self-assessment__actions"}else if(type=="feedback_assess"){container=".submission__feedback__actions"}if(container===null){if(msg!==null){console.log(msg)}}else{var msgHtml=msg===null?"":msg;$(container+" .message__content").html("<p>"+msgHtml+"</p>");$(container).toggleClass("has--error",msg!==null)}},showLoadError:function(step){var container="#openassessment__"+step;$(container).toggleClass("has--error",true);$(container+" .step__status__value i").removeClass().addClass("ico icon-warning-sign");$(container+" .step__status__value .copy").html("Unable to Load")}};function OpenAssessmentBlock(runtime,element){$(function($){var server=new OpenAssessment.Server(runtime,element);var ui=new OpenAssessment.BaseUI(runtime,element,server);ui.load()})}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}OpenAssessment.StudioUI=function(runtime,element,server){this.runtime=runtime;this.server=server;this.codeBox=CodeMirror.fromTextArea($(element).find(".openassessment-editor").first().get(0),{mode:"xml",lineNumbers:true,lineWrapping:true});var ui=this;$(element).find(".openassessment-save-button").click(function(eventData){ui.save()});$(element).find(".openassessment-cancel-button").click(function(eventData){ui.cancel()})};OpenAssessment.StudioUI.prototype={load:function(){var ui=this;this.server.loadXml().done(function(xml){ui.codeBox.setValue(xml)}).fail(function(msg){ui.showError(msg)})},save:function(){var ui=this;this.server.checkReleased().done(function(isReleased){if(isReleased){ui.confirmPostReleaseUpdate($.proxy(ui.updateXml,ui))}else{ui.updateXml()}}).fail(function(errMsg){ui.showError(msg)})},confirmPostReleaseUpdate:function(onConfirm){var msg="This problem has already been released. Any changes will apply only to future assessments.";if(confirm(msg)){onConfirm()}},updateXml:function(){this.runtime.notify("save",{state:"start"});var xml=this.codeBox.getValue();var ui=this;this.server.updateXml(xml).done(function(){ui.runtime.notify("save",{state:"end"});ui.load()}).fail(function(msg){ui.showError(msg)})},cancel:function(){this.runtime.notify("cancel",{})},showError:function(errorMsg){this.runtime.notify("error",{msg:errorMsg})}};function OpenAssessmentEditor(runtime,element){$(function($){var server=new OpenAssessment.Server(runtime,element);var ui=new OpenAssessment.StudioUI(runtime,element,server);ui.load()})}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},maxInputSize:1024*64,render:function(component){var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},renderContinuedPeer:function(){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(this,[data])}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},submit:function(submission){var url=this.url("submit");if(submission.length>this.maxInputSize){return $.Deferred(function(defer){defer.rejectWith(this,["submit","Response text is too large. Please reduce the size of your response and try to submit again."])}).promise()}return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission})}).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(data){defer.rejectWith(this,["AJAX","Could not contact server."])})}).promise()},save:function(submission){var url=this.url("save_submission");if(submission.length>this.maxInputSize){return $.Deferred(function(defer){defer.rejectWith(this,["Response text is too large. Please reduce the size of your response and try to submit again."])}).promise()}return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission})}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback");if(text.length>this.maxInputSize){return $.Deferred(function(defer){defer.rejectWith(this,["Response text is too large. Please reduce the size of your response and try to submit again."])}).promise()}var payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},peerAssess:function(submissionId,optionsSelected,feedback){var url=this.url("peer_assess");if(feedback.length>this.maxInputSize){return $.Deferred(function(defer){defer.rejectWith(this,["Response text is too large. Please reduce the size of your response and try to submit again."])}).promise()}var payload=JSON.stringify({submission_uuid:submissionId,options_selected:optionsSelected,feedback:feedback});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},selfAssess:function(submissionId,optionsSelected){var url=this.url("self_assess");var payload=JSON.stringify({submission_uuid:submissionId,options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})})},loadXml:function(){var url=this.url("xml");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.xml])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},updateXml:function(xml){var url=this.url("update_xml");var payload=JSON.stringify({xml:xml});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},checkReleased:function(){var url=this.url("check_released");var payload='""';return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()}};
\ No newline at end of file
if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}OpenAssessment.BaseView=function(runtime,element,server){this.runtime=runtime;this.element=element;this.server=server};OpenAssessment.BaseView.prototype={scrollToTop:function(){if($.scrollTo instanceof Function){$(window).scrollTo($("#openassessment__steps"),800,{offset:-50})}},setUpCollapseExpand:function(parentSel,onExpand){parentSel.find(".ui-toggle-visibility__control").click(function(eventData){var sel=$(eventData.target).closest(".ui-toggle-visibility");if(sel.hasClass("is--collapsed")&&onExpand!==undefined){onExpand()}sel.toggleClass("is--collapsed")})},load:function(){this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this);this.responseView.load();this.renderPeerAssessmentStep();this.renderSelfAssessmentStep();this.renderGradeStep()},renderPeerAssessmentStep:function(){var view=this;this.server.render("peer_assessment").done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);var sel=$("#openassessment__peer-assessment",view.element);view.setUpCollapseExpand(sel,$.proxy(view.renderContinuedPeerAssessmentStep,view));sel.find("#peer-assessment--001__assessment").change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;$("#peer-assessment--001__assessment__submit",view.element).toggleClass("is--disabled",numChecked!=numAvailable)});sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.peerAssess()})}).fail(function(errMsg){view.showLoadError("peer-assessment")})},renderContinuedPeerAssessmentStep:function(){var view=this;this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);var sel=$("#openassessment__peer-assessment",view.element);view.setUpCollapseExpand(sel);sel.find("#peer-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.continuedPeerAssess()});sel.find("#peer-assessment--001__assessment").change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;$("#peer-assessment--001__assessment__submit",view.element).toggleClass("is--disabled",numChecked!=numAvailable)})}).fail(function(errMsg){view.showLoadError("peer-assessment")})},renderSelfAssessmentStep:function(){var view=this;this.server.render("self_assessment").done(function(html){$("#openassessment__self-assessment",view.element).replaceWith(html);var sel=$("#openassessment__self-assessment",view.element);view.setUpCollapseExpand(sel);$("#self-assessment--001__assessment",view.element).change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;$("#self-assessment--001__assessment__submit",view.element).toggleClass("is--disabled",numChecked!=numAvailable)});sel.find("#self-assessment--001__assessment__submit").click(function(eventObject){eventObject.preventDefault();view.selfAssess()})}).fail(function(errMsg){view.showLoadError("self-assessment")})},renderGradeStep:function(){var view=this;this.server.render("grade").done(function(html){$("#openassessment__grade",view.element).replaceWith(html);var sel=$("#openassessment__grade",view.element);view.setUpCollapseExpand(sel);sel.find("#feedback__submit").click(function(eventObject){eventObject.preventDefault();view.submitFeedbackOnAssessment()})}).fail(function(errMsg){view.showLoadError("grade",errMsg)})},submitFeedbackOnAssessment:function(){var view=this;var text=$("#feedback__remarks__value",view.element).val();var options=$.map($(".feedback__overall__value:checked",view.element),function(element,index){return $(element).val()});view.server.submitFeedbackOnAssessment(text,options).done(function(){}).fail(function(errMsg){view.toggleActionError("feedback_assess",errMsg)})},peerAssess:function(){var view=this;this.peerAssessRequest(function(){view.renderPeerAssessmentStep();view.renderSelfAssessmentStep();view.renderGradeStep();view.scrollToTop()})},continuedPeerAssess:function(){var view=this;view.peerAssessRequest(function(){view.renderContinuedPeerAssessmentStep();view.renderGradeStep()})},peerAssessRequest:function(successFunction){var submissionId=$("span#peer_submission_uuid",this.element)[0].innerHTML.trim();var optionsSelected={};$("#peer-assessment--001__assessment input[type=radio]:checked",this.element).each(function(index,sel){optionsSelected[sel.name]=sel.value});var feedback=$("#assessment__rubric__question--feedback__value",this.element).val();var view=this;this.toggleActionError("peer",null);this.server.peerAssess(submissionId,optionsSelected,feedback).done(successFunction).fail(function(errMsg){view.toggleActionError("peer",errMsg)})},selfAssess:function(){var submissionId=$("span#self_submission_uuid",this.element)[0].innerHTML.trim();var optionsSelected={};$("#self-assessment--001__assessment input[type=radio]:checked",this.element).each(function(index,sel){optionsSelected[sel.name]=sel.value});var view=this;this.toggleActionError("self",null);this.server.selfAssess(submissionId,optionsSelected).done(function(){view.renderPeerAssessmentStep();view.renderSelfAssessmentStep();view.renderGradeStep();view.scrollToTop()}).fail(function(errMsg){view.toggleActionError("self",errMsg)})},toggleActionError:function(type,msg){var container=null;if(type=="save"){container=".response__submission__actions"}else if(type=="submit"){container=".step__actions"}else if(type=="peer"){container=".step__actions"}else if(type=="self"){container=".self-assessment__actions"}else if(type=="feedback_assess"){container=".submission__feedback__actions"}if(container===null){if(msg!==null){console.log(msg)}}else{var msgHtml=msg===null?"":msg;$(container+" .message__content").html("<p>"+msgHtml+"</p>");$(container).toggleClass("has--error",msg!==null)}},showLoadError:function(step){var container="#openassessment__"+step;$(container).toggleClass("has--error",true);$(container+" .step__status__value i").removeClass().addClass("ico icon-warning-sign");$(container+" .step__status__value .copy").html("Unable to Load")}};function OpenAssessmentBlock(runtime,element){$(function($){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server);view.load()})}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}OpenAssessment.StudioView=function(runtime,element,server){this.runtime=runtime;this.server=server;this.codeBox=CodeMirror.fromTextArea($(element).find(".openassessment-editor").first().get(0),{mode:"xml",lineNumbers:true,lineWrapping:true});var view=this;$(element).find(".openassessment-save-button").click(function(eventData){view.save()});$(element).find(".openassessment-cancel-button").click(function(eventData){view.cancel()})};OpenAssessment.StudioView.prototype={load:function(){var view=this;this.server.loadXml().done(function(xml){view.codeBox.setValue(xml)}).fail(function(msg){view.showError(msg)})},save:function(){var view=this;this.server.checkReleased().done(function(isReleased){if(isReleased){view.confirmPostReleaseUpdate($.proxy(view.updateXml,view))}else{view.updateXml()}}).fail(function(errMsg){view.showError(msg)})},confirmPostReleaseUpdate:function(onConfirm){var msg="This problem has already been released. Any changes will apply only to future assessments.";if(confirm(msg)){onConfirm()}},updateXml:function(){this.runtime.notify("save",{state:"start"});var xml=this.codeBox.getValue();var view=this;this.server.updateXml(xml).done(function(){view.runtime.notify("save",{state:"end"});view.load()}).fail(function(msg){view.showError(msg)})},cancel:function(){this.runtime.notify("cancel",{})},showError:function(errorMsg){this.runtime.notify("error",{msg:errorMsg})}};function OpenAssessmentEditor(runtime,element){$(function($){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.StudioView(runtime,element,server);view.load()})}if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}OpenAssessment.ResponseView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.savedResponse=""};OpenAssessment.ResponseView.prototype={load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){view.baseView.showLoadError("response")})},installHandlers:function(){var sel=$("#openassessment__response",this.element);var view=this;this.baseView.setUpCollapseExpand(sel);this.savedResponse=this.response();var handleChange=function(eventData){view.responseChanged()};sel.find("#submission__answer__value").on("change keyup drop paste",handleChange);sel.find("#step--response__submit").click(function(eventObject){eventObject.preventDefault();view.submit()});sel.find("#submission__save").click(function(eventObject){eventObject.preventDefault();view.save()})},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)}},saveEnabled:function(enabled){var sel=$("#submission__save",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{sel.html('<span class="sr">Your Working Submission Status:</span>\n'+msg)}},response:function(text){var sel=$("#submission__answer__value",this.element);if(typeof text==="undefined"){return sel.val()}else{sel.val(text)}},responseChanged:function(){var currentResponse=this.response();var isBlank=currentResponse!=="";this.submitEnabled(isBlank);if(this.savedResponse!==currentResponse){this.saveEnabled(isBlank);this.saveStatus("Unsaved draft")}},save:function(){this.saveStatus("Saving...");this.baseView.toggleActionError("save",null);var view=this;var savedResponse=this.response();this.server.save(savedResponse).done(function(){view.savedResponse=savedResponse;var currentResponse=view.response();view.submitEnabled(currentResponse!=="");if(currentResponse==savedResponse){view.saveEnabled(false);view.saveStatus("Saved but not submitted")}}).fail(function(errMsg){view.saveStatus("Error");view.baseView.toggleActionError("save",errMsg)})},submit:function(){var submission=$("#submission__answer__value",this.element).val();this.baseView.toggleActionError("response",null);var view=this;var baseView=this.baseView;this.server.submit(submission).done(function(studentId,attemptNum){view.load();baseView.renderPeerAssessmentStep()}).fail(function(errCode,errMsg){baseView.toggleActionError("submit",errMsg)})}};if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},maxInputSize:1024*64,render:function(component){var url=this.url("render_"+component);return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html"}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},renderContinuedPeer:function(){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(this,[data])}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},submit:function(submission){var url=this.url("submit");if(submission.length>this.maxInputSize){return $.Deferred(function(defer){defer.rejectWith(this,["submit","Response text is too large. Please reduce the size of your response and try to submit again."])}).promise()}return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission})}).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(data){defer.rejectWith(this,["AJAX","Could not contact server."])})}).promise()},save:function(submission){var url=this.url("save_submission");if(submission.length>this.maxInputSize){return $.Deferred(function(defer){defer.rejectWith(this,["Response text is too large. Please reduce the size of your response and try to submit again."])}).promise()}return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:JSON.stringify({submission:submission})}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},submitFeedbackOnAssessment:function(text,options){var url=this.url("submit_feedback");if(text.length>this.maxInputSize){return $.Deferred(function(defer){defer.rejectWith(this,["Response text is too large. Please reduce the size of your response and try to submit again."])}).promise()}var payload=JSON.stringify({feedback_text:text,feedback_options:options});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},peerAssess:function(submissionId,optionsSelected,feedback){var url=this.url("peer_assess");if(feedback.length>this.maxInputSize){return $.Deferred(function(defer){defer.rejectWith(this,["Response text is too large. Please reduce the size of your response and try to submit again."])}).promise()}var payload=JSON.stringify({submission_uuid:submissionId,options_selected:optionsSelected,feedback:feedback});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},selfAssess:function(submissionId,optionsSelected){var url=this.url("self_assess");var payload=JSON.stringify({submission_uuid:submissionId,options_selected:optionsSelected});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})})},loadXml:function(){var url=this.url("xml");return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.xml])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},updateXml:function(xml){var url=this.url("update_xml");var payload=JSON.stringify({xml:xml});return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()},checkReleased:function(){var url=this.url("check_released");var payload='""';return $.Deferred(function(defer){$.ajax({type:"POST",url:url,data:payload}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,["Could not contact server."])})}).promise()}};
\ No newline at end of file
......@@ -2,7 +2,7 @@
Tests for OA student-facing views.
**/
describe("OpenAssessment.BaseUI", function() {
describe("OpenAssessment.BaseView", function() {
// Stub server that returns dummy data
var StubServer = function() {
......@@ -15,12 +15,6 @@ describe("OpenAssessment.BaseUI", function() {
grade: "Test fragment"
};
this.submit = function(submission) {
return $.Deferred(function(defer) {
defer.resolveWith(this, ['student', 0]);
}).promise();
};
this.peerAssess = function(submissionId, optionsSelected, feedback) {
return $.Deferred(function(defer) { defer.resolve(); }).promise();
};
......@@ -42,7 +36,7 @@ describe("OpenAssessment.BaseUI", function() {
this.feedbackOptions = options;
// Return a promise that always resolves successfully
return $.Deferred(function(defer) { defer.resolve() }).promise();
return $.Deferred(function(defer) { defer.resolve(); }).promise();
};
};
......@@ -50,7 +44,7 @@ describe("OpenAssessment.BaseUI", function() {
var runtime = {};
var server = null;
var ui = null;
var view = null;
/**
Wait for subviews to load before executing callback.
......@@ -60,7 +54,7 @@ describe("OpenAssessment.BaseUI", function() {
**/
var loadSubviews = function(callback) {
runs(function() {
ui.load();
view.load();
});
waitsFor(function() {
......@@ -85,21 +79,13 @@ describe("OpenAssessment.BaseUI", function() {
// Create the object under test
var el = $("#openassessment-base").get(0);
ui = new OpenAssessment.BaseUI(runtime, el, server);
});
it("Sends a submission to the server", function() {
loadSubviews(function() {
spyOn(server, 'submit').andCallThrough();
ui.submit();
expect(server.submit).toHaveBeenCalled();
});
view = new OpenAssessment.BaseView(runtime, el, server);
});
it("Sends a peer assessment to the server", function() {
loadSubviews(function() {
spyOn(server, 'peerAssess').andCallThrough();
ui.peerAssess();
view.peerAssess();
expect(server.peerAssess).toHaveBeenCalled();
});
});
......@@ -107,7 +93,7 @@ describe("OpenAssessment.BaseUI", function() {
it("Sends a self assessment to the server", function() {
loadSubviews(function() {
spyOn(server, 'selfAssess').andCallThrough();
ui.selfAssess();
view.selfAssess();
expect(server.selfAssess).toHaveBeenCalled();
});
});
......@@ -126,10 +112,10 @@ describe("OpenAssessment.BaseUI", function() {
// Create the object under test
var el = $("#openassessment-base").get(0);
ui = new OpenAssessment.BaseUI(runtime, el, server);
view = new OpenAssessment.BaseView(runtime, el, server);
// Submit feedback on an assessment
ui.submitFeedbackOnAssessment();
view.submitFeedbackOnAssessment();
// Expect that the feedback was retrieved from the DOM and sent to the server
expect(server.feedbackText).toEqual('I disliked the feedback I received.');
......
......@@ -2,7 +2,7 @@
Tests for OA XBlock editing.
**/
describe("OpenAssessment.StudioUI", function() {
describe("OpenAssessment.StudioView", function() {
var runtime = {
notify: function(type, data) {}
......@@ -52,7 +52,7 @@ describe("OpenAssessment.StudioUI", function() {
};
var server = null;
var ui = null;
var view = null;
beforeEach(function() {
......@@ -68,24 +68,24 @@ describe("OpenAssessment.StudioUI", function() {
// Create the object under test
var el = $('#openassessment-edit').get(0);
ui = new OpenAssessment.StudioUI(runtime, el, server);
view = new OpenAssessment.StudioView(runtime, el, server);
});
it("loads the XML definition", function() {
// Initialize the UI
ui.load();
// Initialize the view
view.load();
// Expect that the XML definition was loaded
var contents = ui.codeBox.getValue();
var contents = view.codeBox.getValue();
expect(contents).toEqual('<openassessment></openassessment>');
});
it("saves the XML definition", function() {
// Update the XML
ui.codeBox.setValue('<openassessment>test!</openassessment>');
view.codeBox.setValue('<openassessment>test!</openassessment>');
// Save the updated XML
ui.save();
view.save();
// Expect the saving notification to start/end
expect(runtime.notify).toHaveBeenCalledWith('save', {state: 'start'});
......@@ -100,31 +100,31 @@ describe("OpenAssessment.StudioUI", function() {
server.isReleased = true;
// Stub the confirmation step (avoid showing the dialog)
spyOn(ui, 'confirmPostReleaseUpdate').andCallFake(
spyOn(view, 'confirmPostReleaseUpdate').andCallFake(
function(onConfirm) { onConfirm(); }
);
// Save the updated XML
ui.save();
view.save();
// Verify that the user was asked to confirm the changes
expect(ui.confirmPostReleaseUpdate).toHaveBeenCalled();
expect(view.confirmPostReleaseUpdate).toHaveBeenCalled();
});
it("cancels editing", function() {
ui.cancel();
view.cancel();
expect(runtime.notify).toHaveBeenCalledWith('cancel', {});
});
it("displays an error when server reports a load XML error", function() {
server.loadError = true;
ui.load();
view.load();
expect(runtime.notify).toHaveBeenCalledWith('error', {msg: 'Test error'});
});
it("displays an error when server reports an update XML error", function() {
server.updateError = true;
ui.save('<openassessment>test!</openassessment>');
view.save('<openassessment>test!</openassessment>');
expect(runtime.notify).toHaveBeenCalledWith('error', {msg: 'Test error'});
});
......
/**
Tests for OpenAssessment response (submission) step.
**/
describe("OpenAssessment.ResponseView", function() {
// Stub server
var StubServer = function() {
var successPromise = $.Deferred(
function(defer) {
defer.resolve();
}
).promise();
this.save = function(submission) {
return successPromise;
};
this.submit = function(submission) {
return successPromise;
};
this.render = function(step) {
return successPromise;
};
};
// Stub base view
var StubBaseView = function() {
this.showLoadError = function(msg) {};
this.toggleActionError = function(msg, step) {};
this.setUpCollapseExpand = function(sel) {};
this.renderPeerAssessmentStep = function() {};
};
// Stubs
var baseView = null;
var server = null;
// View under test
var view = null;
beforeEach(function() {
// Load the DOM fixture
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_response.html');
// Create the stub server
server = new StubServer();
// Create the stub base view
baseView = new StubBaseView();
// Create and install the view
var el = $('#openassessment-base').get(0);
view = new OpenAssessment.ResponseView(el, server, baseView);
view.installHandlers();
});
it("updates submit/save buttons and save status when response text changes", function() {
// Response is blank --> save/submit buttons disabled
view.response('');
view.responseChanged();
expect(view.submitEnabled()).toBe(false);
expect(view.saveEnabled()).toBe(false);
expect(view.saveStatus()).toContain('Unsaved draft');
// Response is not blank --> submit button enabled
view.response('Test response');
view.responseChanged();
expect(view.submitEnabled()).toBe(true);
expect(view.saveEnabled()).toBe(true);
expect(view.saveStatus()).toContain('Unsaved draft');
});
it("updates submit/save buttons and save status when the user saves a response", function() {
// Response is blank --> save/submit button is disabled
view.response('');
view.save();
expect(view.submitEnabled()).toBe(false);
expect(view.saveEnabled()).toBe(false);
expect(view.saveStatus()).toContain('Saved but not submitted');
// Response is not blank --> submit button enabled
view.response('Test response');
view.save();
expect(view.submitEnabled()).toBe(true);
expect(view.saveEnabled()).toBe(false);
expect(view.saveStatus()).toContain('Saved but not submitted');
});
it("shows unsaved draft only when response text has changed", function() {
// Save the initial response
view.response('Lorem ipsum');
view.save();
expect(view.saveEnabled()).toBe(false);
expect(view.saveStatus()).toContain('Saved but not submitted');
// Keep the text the same, but trigger an update
// Should still be saved
view.response('Lorem ipsum');
view.responseChanged();
expect(view.saveEnabled()).toBe(false);
expect(view.saveStatus()).toContain('Saved but not submitted');
// Change the text
// This should cause it to change to unsaved draft
view.response('changed ');
view.responseChanged();
expect(view.saveEnabled()).toBe(true);
expect(view.saveStatus()).toContain('Unsaved draft');
});
it("sends the saved submission to the server", function() {
spyOn(server, 'save').andCallThrough();
view.response('Test response');
view.save();
expect(server.save).toHaveBeenCalledWith('Test response');
});
it("submits a response to the server", function() {
spyOn(server, 'submit').andCallThrough();
view.response('Test response');
view.submit();
expect(server.submit).toHaveBeenCalledWith('Test response');
});
});
......@@ -15,16 +15,16 @@ Args:
server (OpenAssessment.Server): The interface to the XBlock server.
Returns:
OpenAssessment.BaseUI
OpenAssessment.BaseView
**/
OpenAssessment.BaseUI = function(runtime, element, server) {
OpenAssessment.BaseView = function(runtime, element, server) {
this.runtime = runtime;
this.element = element;
this.server = server;
};
OpenAssessment.BaseUI.prototype = {
OpenAssessment.BaseView.prototype = {
/**
* Checks to see if the scrollTo function is available, then scrolls to the
......@@ -65,91 +65,35 @@ OpenAssessment.BaseUI.prototype = {
* Asynchronously load each sub-view into the DOM.
*/
load: function() {
this.renderSubmissionStep();
this.responseView = new OpenAssessment.ResponseView(this.element, this.server, this);
this.responseView.load();
this.renderPeerAssessmentStep();
this.renderSelfAssessmentStep();
this.renderGradeStep();
},
/**
Render the submission step.
**/
renderSubmissionStep: function() {
var ui = this;
this.server.render('submission').done(
function(html) {
// Load the HTML
$('#openassessment__response', ui.element).replaceWith(html);
var sel = $('#openassessment__response', ui.element);
// Install a click handler for collapse/expand
ui.setUpCollapseExpand(sel);
// If we have a saved submission, enable the submit button
ui.responseChanged();
// Install change handler for textarea (to enable submission button)
sel.find('#submission__answer__value').keyup(
function(eventData) { ui.responseChanged(); }
);
// Install a click handler for submission
sel.find('#step--response__submit').click(
function(eventObject) {
// Override default form submission
eventObject.preventDefault();
ui.submit();
}
);
// Install a click handler for the save button
sel.find('#submission__save').click(
function(eventObject) {
// Override default form submission
eventObject.preventDefault();
ui.save();
}
);
}
).fail(function(errMsg) {
ui.showLoadError('response');
});
},
/**
Enable/disable the submission and save buttons based on whether
the user has entered a response.
**/
responseChanged: function() {
var blankSubmission = ($('#submission__answer__value', this.element).val() === '');
$('#step--response__submit', this.element).toggleClass('is--disabled', blankSubmission);
$('#submission__save', this.element).toggleClass('is--disabled', blankSubmission);
},
/**
Render the peer-assessment step.
**/
renderPeerAssessmentStep: function() {
var ui = this;
var view = this;
this.server.render('peer_assessment').done(
function(html) {
// Load the HTML
$('#openassessment__peer-assessment', ui.element).replaceWith(html);
var sel = $('#openassessment__peer-assessment', ui.element);
$('#openassessment__peer-assessment', view.element).replaceWith(html);
var sel = $('#openassessment__peer-assessment', view.element);
// Install a click handler for collapse/expand
ui.setUpCollapseExpand(sel, $.proxy(ui.renderContinuedPeerAssessmentStep, ui));
view.setUpCollapseExpand(sel, $.proxy(view.renderContinuedPeerAssessmentStep, view));
// Install a change handler for rubric options to enable/disable the submit button
sel.find("#peer-assessment--001__assessment").change(
function() {
var numChecked = $('input[type=radio]:checked', this).length;
var numAvailable = $('.field--radio.assessment__rubric__question', this).length;
$("#peer-assessment--001__assessment__submit", ui.element).toggleClass(
$("#peer-assessment--001__assessment__submit", view.element).toggleClass(
'is--disabled', numChecked != numAvailable
);
}
......@@ -162,13 +106,13 @@ OpenAssessment.BaseUI.prototype = {
eventObject.preventDefault();
// Handle the click
ui.peerAssess();
view.peerAssess();
}
);
}
).fail(function(errMsg) {
ui.showLoadError('peer-assessment');
view.showLoadError('peer-assessment');
});
},
......@@ -178,16 +122,16 @@ OpenAssessment.BaseUI.prototype = {
* peer grading process.
*/
renderContinuedPeerAssessmentStep: function() {
var ui = this;
var view = this;
this.server.renderContinuedPeer().done(
function(html) {
// Load the HTML
$('#openassessment__peer-assessment', ui.element).replaceWith(html);
var sel = $('#openassessment__peer-assessment', ui.element);
$('#openassessment__peer-assessment', view.element).replaceWith(html);
var sel = $('#openassessment__peer-assessment', view.element);
// Install a click handler for collapse/expand
ui.setUpCollapseExpand(sel);
view.setUpCollapseExpand(sel);
// Install a click handler for assessment
sel.find('#peer-assessment--001__assessment__submit').click(
......@@ -196,7 +140,7 @@ OpenAssessment.BaseUI.prototype = {
eventObject.preventDefault();
// Handle the click
ui.continuedPeerAssess();
view.continuedPeerAssess();
}
);
......@@ -205,14 +149,14 @@ OpenAssessment.BaseUI.prototype = {
function() {
var numChecked = $('input[type=radio]:checked', this).length;
var numAvailable = $('.field--radio.assessment__rubric__question', this).length;
$("#peer-assessment--001__assessment__submit", ui.element).toggleClass(
$("#peer-assessment--001__assessment__submit", view.element).toggleClass(
'is--disabled', numChecked != numAvailable
);
}
);
}
).fail(function(errMsg) {
ui.showLoadError('peer-assessment');
view.showLoadError('peer-assessment');
});
},
......@@ -220,23 +164,23 @@ OpenAssessment.BaseUI.prototype = {
Render the self-assessment step.
**/
renderSelfAssessmentStep: function() {
var ui = this;
var view = this;
this.server.render('self_assessment').done(
function(html) {
// Load the HTML
$('#openassessment__self-assessment', ui.element).replaceWith(html);
var sel = $('#openassessment__self-assessment', ui.element);
$('#openassessment__self-assessment', view.element).replaceWith(html);
var sel = $('#openassessment__self-assessment', view.element);
// Install a click handler for collapse/expand
ui.setUpCollapseExpand(sel);
view.setUpCollapseExpand(sel);
// Install a change handler for rubric options to enable/disable the submit button
$("#self-assessment--001__assessment", ui.element).change(
$("#self-assessment--001__assessment", view.element).change(
function() {
var numChecked = $('input[type=radio]:checked', this).length;
var numAvailable = $('.field--radio.assessment__rubric__question', this).length;
$("#self-assessment--001__assessment__submit", ui.element).toggleClass(
$("#self-assessment--001__assessment__submit", view.element).toggleClass(
'is--disabled', numChecked != numAvailable
);
}
......@@ -249,12 +193,12 @@ OpenAssessment.BaseUI.prototype = {
eventObject.preventDefault();
// Handle the click
ui.selfAssess();
view.selfAssess();
}
);
}
).fail(function(errMsg) {
ui.showLoadError('self-assessment');
view.showLoadError('self-assessment');
});
},
......@@ -262,116 +206,71 @@ OpenAssessment.BaseUI.prototype = {
Render the grade step.
**/
renderGradeStep: function() {
var ui = this;
var view = this;
this.server.render('grade').done(
function(html) {
// Load the HTML
$('#openassessment__grade', ui.element).replaceWith(html);
$('#openassessment__grade', view.element).replaceWith(html);
// Install a click handler for collapse/expand
var sel = $('#openassessment__grade', ui.element);
ui.setUpCollapseExpand(sel);
var sel = $('#openassessment__grade', view.element);
view.setUpCollapseExpand(sel);
// Install a click handler for assessment feedback
sel.find('#feedback__submit').click(function(eventObject) {
eventObject.preventDefault();
ui.submitFeedbackOnAssessment();
view.submitFeedbackOnAssessment();
});
}
).fail(function(errMsg) {
ui.showLoadError('grade', errMsg);
view.showLoadError('grade', errMsg);
});
},
/**
Save a response without submitting it.
**/
save: function() {
// Retrieve the student's response from the DOM
var ui = this;
var submission = $('#submission__answer__value', ui.element).val();
ui.setSaveStatus('Saving...');
ui.toggleActionError('save', null);
ui.server.save(submission).done(function() {
ui.setSaveStatus("Saved but not submitted");
}).fail(function(errMsg) {
ui.setSaveStatus('Error');
ui.toggleActionError('save', errMsg);
});
},
/**
Display a save status message.
Args:
msg (str): The message to display.
**/
setSaveStatus: function(msg) {
$('#response__save_status h3', this.element).html(msg);
},
/**
Send a submission to the server and update the UI.
**/
submit: function() {
// Send the submission to the server
var ui = this;
var submission = $('#submission__answer__value', ui.element).val();
ui.toggleActionError('response', null);
ui.server.submit(submission).done(
// When we have successfully sent the submission, expand the next step
function(studentId, attemptNum) {
ui.renderSubmissionStep();
ui.renderPeerAssessmentStep();
}
).fail(function(errCode, errMsg) {
ui.toggleActionError('submit', errMsg);
});
},
/**
Send assessment feedback to the server and update the UI.
Send assessment feedback to the server and update the view.
**/
submitFeedbackOnAssessment: function() {
// Send the submission to the server
var ui = this;
var text = $('#feedback__remarks__value', ui.element).val();
var view = this;
var text = $('#feedback__remarks__value', view.element).val();
var options = $.map(
$('.feedback__overall__value:checked', ui.element),
$('.feedback__overall__value:checked', view.element),
function(element, index) { return $(element).val(); }
);
ui.server.submitFeedbackOnAssessment(text, options).done(function() {
view.server.submitFeedbackOnAssessment(text, options).done(function() {
// When we have successfully sent the submission, textarea no longer editable
// TODO
// When we have successfully sent the submission, textarea no longer editable
// console.log("Feedback to the assessments submitted, thanks!");
}).fail(function(errMsg) {
ui.toggleActionError('feedback_assess', errMsg);
view.toggleActionError('feedback_assess', errMsg);
});
},
/**
Send an assessment to the server and update the UI.
Send an assessment to the server and update the view.
**/
peerAssess: function() {
var ui = this;
ui.peerAssessRequest(function() {
ui.renderPeerAssessmentStep();
ui.renderSelfAssessmentStep();
ui.renderGradeStep();
ui.scrollToTop();
var view = this;
this.peerAssessRequest(function() {
view.renderPeerAssessmentStep();
view.renderSelfAssessmentStep();
view.renderGradeStep();
view.scrollToTop();
});
},
/**
* Send an assessment to the server and update the UI, with the assumption
* Send an assessment to the server and update the view, with the assumption
* that we are continuing peer assessments beyond the required amount.
*/
continuedPeerAssess: function() {
var ui = this;
ui.peerAssessRequest(function() {
ui.renderContinuedPeerAssessmentStep();
ui.renderGradeStep();
var view = this;
view.peerAssessRequest(function() {
view.renderContinuedPeerAssessmentStep();
view.renderGradeStep();
});
},
......@@ -396,17 +295,17 @@ OpenAssessment.BaseUI.prototype = {
var feedback = $('#assessment__rubric__question--feedback__value', this.element).val();
// Send the assessment to the server
var ui = this;
var view = this;
this.toggleActionError('peer', null);
this.server.peerAssess(submissionId, optionsSelected, feedback).done(
successFunction
).fail(function(errMsg) {
ui.toggleActionError('peer', errMsg);
view.toggleActionError('peer', errMsg);
});
},
/**
Send a self-assessment to the server and update the UI.
Send a self-assessment to the server and update the view.
**/
selfAssess: function() {
// Retrieve self-assessment info from the DOM
......@@ -419,17 +318,17 @@ OpenAssessment.BaseUI.prototype = {
);
// Send the assessment to the server
var ui = this;
var view = this;
this.toggleActionError('self', null);
this.server.selfAssess(submissionId, optionsSelected).done(
function() {
ui.renderPeerAssessmentStep();
ui.renderSelfAssessmentStep();
ui.renderGradeStep();
ui.scrollToTop();
view.renderPeerAssessmentStep();
view.renderSelfAssessmentStep();
view.renderGradeStep();
view.scrollToTop();
}
).fail(function(errMsg) {
ui.toggleActionError('self', errMsg);
view.toggleActionError('self', errMsg);
});
},
......@@ -486,7 +385,7 @@ function OpenAssessmentBlock(runtime, element) {
**/
$(function($) {
var server = new OpenAssessment.Server(runtime, element);
var ui = new OpenAssessment.BaseUI(runtime, element, server);
ui.load();
var view = new OpenAssessment.BaseView(runtime, element, server);
view.load();
});
}
......@@ -8,7 +8,7 @@ if (typeof OpenAssessment == "undefined" || !OpenAssessment) {
/**
Interface for editing UI in Studio.
Interface for editing view in Studio.
The constructor initializes the DOM for editing.
Args:
......@@ -17,9 +17,9 @@ Args:
server (OpenAssessment.Server): The interface to the XBlock server.
Returns:
OpenAssessment.StudioUI
OpenAssessment.StudioView
**/
OpenAssessment.StudioUI = function(runtime, element, server) {
OpenAssessment.StudioView = function(runtime, element, server) {
this.runtime = runtime;
this.server = server;
......@@ -30,31 +30,31 @@ OpenAssessment.StudioUI = function(runtime, element, server) {
);
// Install click handlers
var ui = this;
var view = this;
$(element).find('.openassessment-save-button').click(
function(eventData) {
ui.save();
view.save();
});
$(element).find('.openassessment-cancel-button').click(
function(eventData) {
ui.cancel();
view.cancel();
});
};
OpenAssessment.StudioUI.prototype = {
OpenAssessment.StudioView.prototype = {
/**
Load the XBlock XML definition from the server and display it in the UI.
Load the XBlock XML definition from the server and display it in the view.
**/
load: function() {
var ui = this;
var view = this;
this.server.loadXml().done(
function(xml) {
ui.codeBox.setValue(xml);
view.codeBox.setValue(xml);
}).fail(function(msg) {
ui.showError(msg);
view.showError(msg);
}
);
},
......@@ -64,17 +64,17 @@ OpenAssessment.StudioUI.prototype = {
If the problem has been released, make the user confirm the save.
**/
save: function() {
var ui = this;
var view = this;
// Check whether the problem has been released; if not,
// warn the user and allow them to cancel.
this.server.checkReleased().done(
function(isReleased) {
if (isReleased) { ui.confirmPostReleaseUpdate($.proxy(ui.updateXml, ui)); }
else { ui.updateXml(); }
if (isReleased) { view.confirmPostReleaseUpdate($.proxy(view.updateXml, view)); }
else { view.updateXml(); }
}
).fail(function(errMsg) {
ui.showError(msg);
view.showError(msg);
});
},
......@@ -102,16 +102,16 @@ OpenAssessment.StudioUI.prototype = {
// Send the updated XML to the server
var xml = this.codeBox.getValue();
var ui = this;
var view = this;
this.server.updateXml(xml).done(function() {
// Notify the client-side runtime that we finished saving
// so it can hide the "Saving..." notification.
ui.runtime.notify('save', {state: 'end'});
view.runtime.notify('save', {state: 'end'});
// Reload the XML definition in the editor
ui.load();
view.load();
}).fail(function(msg) {
ui.showError(msg);
view.showError(msg);
});
},
......@@ -143,7 +143,7 @@ function OpenAssessmentEditor(runtime, element) {
**/
$(function($) {
var server = new OpenAssessment.Server(runtime, element);
var ui = new OpenAssessment.StudioUI(runtime, element, server);
ui.load();
var view = new OpenAssessment.StudioView(runtime, element, server);
view.load();
});
}
/* JavaScript for response (submission) view */
/* Namespace for open assessment */
if (typeof OpenAssessment == "undefined" || !OpenAssessment) {
OpenAssessment = {};
}
/**
Interface for response (submission) view.
Args:
element (DOM element): The DOM element representing the XBlock.
server (OpenAssessment.Server): The interface to the XBlock server.
baseView (OpenAssessment.BaseView): Container view.
Returns:
OpenAssessment.ResponseView
**/
OpenAssessment.ResponseView = function(element, server, baseView) {
this.element = element;
this.server = server;
this.baseView = baseView;
this.savedResponse = "";
};
OpenAssessment.ResponseView.prototype = {
/**
Load the response (submission) view.
**/
load: function() {
var view = this;
this.server.render('submission').done(
function(html) {
// Load the HTML and install event handlers
$('#openassessment__response', view.element).replaceWith(html);
view.installHandlers();
}
).fail(function(errMsg) {
view.baseView.showLoadError('response');
});
},
/**
Install event handlers for the view.
**/
installHandlers: function() {
var sel = $('#openassessment__response', this.element);
var view = this;
// Install a click handler for collapse/expand
this.baseView.setUpCollapseExpand(sel);
// Install change handler for textarea (to enable submission button)
this.savedResponse = this.response();
var handleChange = function(eventData) { view.responseChanged(); };
sel.find('#submission__answer__value').on('change keyup drop paste', handleChange);
// Install a click handler for submission
sel.find('#step--response__submit').click(
function(eventObject) {
// Override default form submission
eventObject.preventDefault();
view.submit();
}
);
// Install a click handler for the save button
sel.find('#submission__save').click(
function(eventObject) {
// Override default form submission
eventObject.preventDefault();
view.save();
}
);
},
/**
Enable/disable the submit button.
Check that whether the submit button is enabled.
Args:
enabled (bool): If specified, set the state of the button.
Returns:
bool: Whether the button is enabled.
Examples:
>> view.submitEnabled(true); // enable the button
>> view.submitEnabled(); // check whether the button is enabled
>> true
**/
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)
}
},
/**
Enable/disable the save button.
Check that whether the save button is enabled.
Args:
enabled (bool): If specified, set the state of the button.
Returns:
bool: Whether the button is enabled.
Examples:
>> view.submitEnabled(true); // enable the button
>> view.submitEnabled(); // check whether the button is enabled
>> true
**/
saveEnabled: function(enabled) {
var sel = $('#submission__save', this.element);
if (typeof enabled === 'undefined') {
return !sel.hasClass('is--disabled');
} else {
sel.toggleClass('is--disabled', !enabled);
}
},
/**
Set the save status message.
Retrieve the save status message.
Args:
msg (string): If specified, the message to display.
Returns:
string: The current status message.
**/
saveStatus: function(msg) {
var sel = $('#response__save_status h3', this.element);
if (typeof msg === 'undefined') {
return sel.text();
} else {
// Setting the HTML will overwrite the screen reader tag,
// so prepend it to the message.
sel.html('<span class="sr">Your Working Submission Status:</span>\n' + msg);
}
},
/**
Set the response text.
Retrieve the response text.
Args:
text (string): If specified, the text to set for the response.
Returns:
string: The current response text.
**/
response: function(text) {
var sel = $('#submission__answer__value', this.element);
if (typeof text === 'undefined') {
return sel.val();
} else {
sel.val(text);
}
},
/**
Enable/disable the submission and save buttons based on whether
the user has entered a response.
**/
responseChanged: function() {
// Enable the save/submit button only for non-blank responses
var currentResponse = this.response();
var isBlank = (currentResponse !== '');
this.submitEnabled(isBlank);
// Update the save button and status only if the response has changed
if (this.savedResponse !== currentResponse) {
this.saveEnabled(isBlank);
this.saveStatus('Unsaved draft');
}
},
/**
Save a response without submitting it.
**/
save: function() {
// Update the save status and error notifications
this.saveStatus('Saving...');
this.baseView.toggleActionError('save', null);
var view = this;
var savedResponse = this.response();
this.server.save(savedResponse).done(function() {
// Remember which response we saved, once the server confirms that it's been saved...
view.savedResponse = savedResponse;
// ... but update the UI based on what the user may have entered
// since hitting the save button.
var currentResponse = view.response();
view.submitEnabled(currentResponse !== '');
if (currentResponse == savedResponse) {
view.saveEnabled(false);
view.saveStatus("Saved but not submitted");
}
}).fail(function(errMsg) {
view.saveStatus('Error');
view.baseView.toggleActionError('save', errMsg);
});
},
/**
Send a response submission to the server and update the view.
**/
submit: function() {
// Send the submission to the server
var submission = $('#submission__answer__value', this.element).val();
this.baseView.toggleActionError('response', null);
var view = this;
var baseView = this.baseView;
this.server.submit(submission).done(
// When we have successfully sent the submission, move on to the next step
function(studentId, attemptNum) {
view.load();
baseView.renderPeerAssessmentStep();
}
).fail(function(errCode, errMsg) {
baseView.toggleActionError('submit', errMsg);
});
}
};
......@@ -155,7 +155,7 @@ class SubmissionMixin(object):
Returns:
unicode
"""
return _(u'Saved but not submitted') if self.has_saved else _(u'Not saved')
return _(u'Saved but not submitted') if self.has_saved else _(u'Unsaved draft')
@XBlock.handler
def render_submission(self, data, suffix=''):
......@@ -185,6 +185,7 @@ class SubmissionMixin(object):
context = {
"saved_response": self.saved_response,
"save_status": self.save_status,
"submit_enabled": self.saved_response != '',
"submission_due": sub_due,
}
......
......@@ -14,7 +14,7 @@ class SaveResponseTest(XBlockHandlerTestCase):
def test_default_saved_response_blank(self, xblock):
resp = self.request(xblock, 'render_submission', json.dumps({}))
self.assertIn('<textarea id="submission__answer__value" placeholder=""></textarea>', resp)
self.assertIn('Not saved', resp)
self.assertIn('Unsaved draft', resp)
@ddt.file_data('data/save_responses.json')
@scenario('data/save_scenario.xml', user_id="Perleman")
......@@ -57,4 +57,4 @@ class SaveResponseTest(XBlockHandlerTestCase):
def test_missing_submission_key(self, xblock):
resp = self.request(xblock, 'save_submission', json.dumps({}), response_format="json")
self.assertFalse(resp['success'])
self.assertIn('submission', resp['msg'])
\ No newline at end of file
self.assertIn('submission', resp['msg'])
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