Commit a93984c0 by Will Daly

Integrate container and container item into the rubric editing view.

Minify CSS.
parent 7b793ad4
{% load i18n %}
{% spaceless %}
<li class="openassessment_criterion is-collapsible">
<div class="openassessment_criterion_header view-outline">
<a class="action expand-collapse collapse"><i class="icon-caret-down ui-toggle-expansion"></i></a>
<h6 class="openassessment_criterion_header_title">{% trans "Criterion" %}</h6>
<div class="openassessment_rubric_remove_button"><h2>{% trans "Remove" %}</h2></div>
</div>
<div class="openassessment_criterion_body wrapper-comp-settings">
<ul class="list-input settings-list openassessment_criterion_basic_editor">
<li class="field comp-setting-entry">
<div class="wrapper-comp-settings">
<label class="openassessment_criterion_name_label setting-label">
{% trans "Criterion Name" %}
<input
class="openassessment_criterion_name input setting-input"
type="text"
value="{{ criterion_name }}"
>
</label>
</div>
</li>
<li class="field comp-setting-entry">
<div class="wrapper-comp-settings">
<label class="openassessment_criterion_prompt_label setting-label">
{% trans "Criterion Prompt" %}
<textarea class="openassessment_criterion_prompt setting-input">{{ criterion_prompt }}</textarea>
</label>
</div>
</li>
</ul>
<ul class="openassessment_criterion_option_list">
{% for option in criterion_options %}
{% include "openassessmentblock/edit/oa_edit_option.html" with option_name=option.name option_points=option.points option_explanation=option.explanation %}
{% endfor %}
</ul>
<div class="openassessment_criterion_add_option openassessment_option_header">
<h2>{% trans "Add Another Option"%}</h2>
</div>
<div class="openassessment_criterion_feedback_wrapper wrapper-comp-settings">
<ul class="list-input settings-list">
<li class="field comp-setting-entry">
<div class="wrapper-comp-setting">
<label class="setting-label">
{% trans "Criterion Feedback" %}
<select class="openassessment_criterion_feedback input setting-input">
<option value="disabled">{% trans "Disabled" %}</option>
<option value="optional" {% if criterion_feedback == "optional" %} selected="true" {% endif %}>{% trans "Optional" %}</option>
<option value="required" {% if criterion_feedback == "required" %} selected="true" {% endif %}>{% trans "Required" %}</option>
</select>
</label>
</div>
<p class="setting-help">
{% trans "Select one of the options above. This describes whether or not the student will have to provide criterion feedback." %}
</p>
</li>
</ul>
</div>
</div>
</li>
{% endspaceless %}
\ No newline at end of file
{% load i18n %}
{% spaceless %}
<li class="openassessment_criterion_option">
<div class="openassessment_option_header">
<span class="openassessment_option_header_title">{% trans "Option" %}</span>
<div class="openassessment_rubric_remove_button">
<h2>{% trans "Remove" %}</h2>
</div>
</div>
<div class="wrapper-comp-settings">
<ul class="list-input settings-list">
<li class="field comp-setting-entry openassessment_criterion_option_name_wrapper">
<div class="wrapper-comp-setting">
<label class="openassessment_criterion_option_name_label setting-label">
{% trans "Option Name"%}
<input
class="openassessment_criterion_option_name input input-label"
type="text"
value="{{ option_name }}"
>
</label>
</div>
</li>
<li class="field comp-setting-entry openassessment_criterion_option_point_wrapper">
<div class="wrapper-comp-setting">
<label class="openassessment_criterion_option_points_label setting-label">
{% trans "Option Points"%}
<input
class="openassessment_criterion_option_points input setting-input"
type="number"
value="{{ option_points }}"
min="0"
>
</label>
</div>
</li>
<li class="field comp-setting-entry openassessment_criterion_option_explanation_wrapper">
<div class="wrapper-comp-setting">
<label class="openassessment_criterion_option_explanation_label setting-label">
{% trans "Option Explanation"%}
<textarea class="openassessment_criterion_option_explanation setting-input">{{ option_explanation }}</textarea>
</label>
</div>
</li>
</ul>
</div>
</li>
{% endspaceless %}
\ No newline at end of file
{% load i18n %} {% load i18n %}
{% spaceless %} {% spaceless %}
<div id="oa_rubric_editor_wrapper" class="oa_editor_content_wrapper"> <div id="oa_rubric_editor_wrapper" class="oa_editor_content_wrapper">
<div id="openassessment_criterion_template" class="is--hidden">
{% include "openassessmentblock/edit/oa_edit_criterion.html" with criterion_name="" criterion_prompt="" criterion_options=False criterion_feedback="disabled" %}
</div>
<div id="openassessment_option_template" class="is--hidden">
{% include "openassessmentblock/edit/oa_edit_option.html" with option_name="" option_points="" option_explanation="" %}
</div>
<div id="openassessment_rubric_instructions"> <div id="openassessment_rubric_instructions">
<p class = openassessment_description> <p class = openassessment_description>
...@@ -9,142 +16,20 @@ ...@@ -9,142 +16,20 @@
</div> </div>
<ul id="openassessment_criterion_list" > <ul id="openassessment_criterion_list" >
{% for criterion in criteria %} {% for criterion in criteria %}
{% with criterion_num=forloop.counter %} {% include "openassessmentblock/edit/oa_edit_criterion.html" with criterion_name=criterion.name criterion_prompt=criterion.prompt criterion_options=criterion.options criterion_feedback=criterion.feedback %}
<li class="openassessment_criterion is-collapsible" id="openassessment_criterion_1">
<div class="openassessment_criterion_header view-outline" id="openassessment_criterion_header_{{ criterion_num }}">
<a class="action expand-collapse collapse">
<i class="icon-caret-down ui-toggle-expansion"></i>
</a>
<h6 class="openassessment_criterion_header_title">
{% blocktrans %} Criterion {{ criterion_num }}{% endblocktrans %}
</h6>
<div class="openassessment_rubric_remove_button" id="openassessment_criterion_{{ criterion_num }}_remove">
<h2>{% trans "Remove" %}</h2>
</div>
</div>
<div class="openassessment_criterion_body wrapper-comp-settings" id="openassessment_criterion_body_{{ criterion_num }}">
<ul class="list-input settings-list openassessment_criterion_basic_editor">
<li class="field comp-setting-entry">
<div class="wrapper-comp-settings">
<label for="openassessment_criterion_{{ criterion_num }}_name" class="openassessment_criterion_name_label setting-label">
{% trans "Criterion Name"%}
</label>
<input id="openassessment_criterion_{{ criterion_num }}_name" class="openassessment_criterion_name input setting-input" type="text" value={{ criterion.name }}>
</div>
</li>
<li class="field comp-setting-entry">
<div class="wrapper-comp-settings">
<label for="openassessment_criterion_{{ criterion_num }}_prompt" class="openassessment_criterion_prompt_label setting-label">
{% trans "Criterion Prompt"%}
</label>
<textarea id="openassessment_criterion_{{ criterion_num }}_prompt" class="openassessment_criterion_prompt setting-input">{{ criterion.prompt }}</textarea>
</div>
</li>
</ul>
<ul id="openassessment_criterion_{{ criterion_num }}_options" class="openassessment_criterion_option_list">
{% for option in criterion.options %}
{% with option_num=forloop.counter %}
<li id=openassessment_criterion_{{ criterion_num }}_option_{{ option_num }} class="openassessment_criterion_option">
<div class="openassessment_option_header">
<span class="openassessment_option_header_title">
{% blocktrans %} Option {{ option_num }}{% endblocktrans %}
</span>
<div class="openassessment_rubric_remove_button" id="openassessment_criterion_{{ criterion_num }}_option_1_remove">
<h2>{% trans "Remove" %}</h2>
</div>
</div>
<div class="wrapper-comp-settings">
<ul class="list-input settings-list">
<li class="field comp-setting-entry openassessment_criterion_option_name_wrapper">
<div class="wrapper-comp-setting">
<label for="openassessment_criterion_{{ criterion_num }}_option_{{ option_num }}_name"
class="openassessment_criterion_option_name_label setting-label">
{% trans "Option Name"%}
</label>
<input id="openassessment_criterion_{{ criterion_num }}_option_{{ option_num }}_name"
class="openassessment_criterion_option_name input input-label"
type="text"
value="{{ option.name }}">
</div>
</li>
<li class="field comp-setting-entry openassessment_criterion_option_point_wrapper">
<div class="wrapper-comp-setting">
<label for="openassessment_criterion_{{ criterion_num }}_option_{{ option_num }}_points"
class="openassessment_criterion_option_points_label setting-label">
{% trans "Option Points"%}
</label>
<input id="openassessment_criterion_{{ criterion_num }}_option_{{ option_num }}_points"
class="openassessment_criterion_option_points input setting-input"
type="number"
value="{{ option.points }}">
</div>
</li>
<li class="field comp-setting-entry openassessment_criterion_option_explanation_wrapper">
<div class="wrapper-comp-setting">
<label for="openassessment_criterion_{{ criterion_num }}_option_{{ option_num }}_explanation"
class="openassessment_criterion_option_explanation_label setting-label">
{% trans "Option Explanation"%}
</label>
<textarea id="openassessment_criterion_{{ criterion_num }}_option_{{ option_num }}_explanation"
class="openassessment_criterion_option_explanation setting-input">{{ option.explanation }}</textarea>
</div>
</li>
</ul>
</div>
</li>
{% endwith %}
{% endfor %}
</ul>
<div id="openassessment_criterion_{{ criterion_num }}_add_option" class="openassessment_criterion_add_option openassessment_option_header">
<h2>{% trans "Add Another Option"%}</h2>
</div>
<div id="openassessment_criterion_{{ criterion_num }}_feedback_wrapper" class="openassessment_criterion_feedback_wrapper wrapper-comp-settings">
<ul class="list-input settings-list">
<li class="field comp-setting-entry">
<div class="wrapper-comp-setting">
<label for="openassessment_criterion_{{ criterion_num }}_feedback" class="setting-label">{% trans "Criterion Feeedback" %}</label>
<select id="openassessment_criterion_{{ criterion_num }}_feedback" class="input setting-input">
<option value="disabled">{% trans "Disabled" %}</option>
<option value="optional" {% if criterion.feedbackprompt == "optional" %} selected="true" {% endif %}>{% trans "Optional" %}</option>
<option value="required" {% if criterion.feedbackprompt == "required" %} selected="true" {% endif %}>{% trans "Required" %}</option>
</select>
</div>
<p class="setting-help">
{% trans "Select one of the options above. This describes whether or not the student will have to provide criterion feedback." %}
</p>
</li>
</ul>
</div>
</div>
</li>
{% endwith %}
{% endfor %} {% endfor %}
</ul> </ul>
<div id="openassessment_rubric_add_criterion"> <div id="openassessment_rubric_add_criterion">
<h6> <h6>
{% trans "Add Another Criterion"%} {% trans "Add Another Criterion" %}
</h6> </h6>
</div> </div>
<div id="openassessment_rubric_feedback_wrapper" class="wrapper-comp-settings"> <div id="openassessment_rubric_feedback_wrapper" class="wrapper-comp-settings">
<div id="openassessment_rubric_feedback_header_open"> <div id="openassessment_rubric_feedback_header">
<span> <span>{% trans "Rubric Feedback" %}</span>
{% trans "Rubric Feedback" %}
</span>
<div class="openassessment_rubric_remove_button" id="openassessment_rubric_feedback_remove">
<h2>{% trans "Remove" %}</h2>
</div>
<div id="openassessment_rubric_feedback_header_closed">
<h2>
{% trans "Add Rubric Feedback" %}
</h2>
</div>
</div> </div>
<ul class="list-input settings-list"> <ul class="list-input settings-list">
<li class="field comp-setting-entry"> <li class="field comp-setting-entry">
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -385,44 +385,46 @@ ...@@ -385,44 +385,46 @@
"title": "The most important of all questions.", "title": "The most important of all questions.",
"submission_due": "2014-10-1T10:00:00", "submission_due": "2014-10-1T10:00:00",
"criteria": [ "criteria": [
{ {
"name": "Criterion 1", "name": "Criterion with two options",
"prompt": "Prompt 1", "prompt": "Prompt for criterion with two options",
"order_num": 0, "order_num": 0,
"feedback": "optional", "feedback": "disabled",
"options": [ "options": [
{ {
"order_num": 2, "order_num": 0,
"points": 1,
"name": "Fair",
"explanation": "Fair explanation"
},
{
"order_num": 1,
"points": 2, "points": 2,
"name": "Good" "name": "Good",
"explanation": "Good explanation"
} }
], ],
"points_possible": 2 "points_possible": 2
}, },
{ {
"name": "Criterion 2", "name": "Criterion with no options",
"prompt": "Prompt 2", "prompt": "Prompt for criterion with no options",
"order_num": 1, "order_num": 0,
"options": [ "options": [],
{ "feedback": "required",
"order_num": 1, "points_possible": 0
"points": 1,
"name": "Fair"
}
],
"points_possible": 2
}, },
{ {
"name": "Criterion 3", "name": "Criterion with optional feedback",
"prompt": "Prompt 3", "prompt": "Prompt for criterion with optional feedback",
"order_num": 2, "order_num": 2,
"feedback": "optional", "feedback": "optional",
"options": [ "options": [
{ {
"order_num": 2, "order_num": 0,
"points": 2, "points": 2,
"name": "Good" "name": "Good",
"explanation": "Good explanation"
} }
], ],
"points_possible": 2 "points_possible": 2
......
if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}OpenAssessment.BaseView=function(runtime,element,server){this.runtime=runtime;this.element=element;this.server=server;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this);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.gradeView=new OpenAssessment.GradeView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffInfoView=new OpenAssessment.StaffInfoView(this.element,this.server,this)};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.load();this.loadAssessmentModules();this.staffInfoView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.selfView.load();this.gradeView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,msg){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"}if(container===null){if(msg!==null){console.log(msg)}}else{var msgHtml=msg===null?"":msg;$(container+" .message__content",element).html("<p>"+msgHtml+"</p>");$(container,element).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(gettext("Unable to Load"))}};function OpenAssessmentBlock(runtime,element){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server);view.load()}OpenAssessment.Container=function(element,selectorDictionary){this.element=element;this.selectorDictionary=selectorDictionary;this.installDeleteButtons(element)};OpenAssessment.Container.prototype={add:function(selectorString){var type=this.selectorDictionary[selectorString];if(type==undefined){throw"The string: ("+selectorString+") is not known by this container."}this.element.append(this.getHtmlTemplate(selectorString))},remove:function(index){var count=0;$(this.element).children().each(function(){if(count==index){$(this).remove();return}count++})},installDeleteButtons:function(liveElement){$(".openassessment_delete_button",liveElement).each(function(){$(this).click(function(){liveElement.closest(".openassessment_deletable").remove()})})},getItemValues:function(){var values=[];var container=this;$(this.element).children().each(function(){var classes=$(this).attr("class").split(/\s+/);var type=undefined;for(var i=0;i<classes.length;i++){var c=classes[i];if(container.selectorDictionary[c]!=undefined){type=container.selectorDictionary[c]}}if(type==undefined){throw"An item with classes ("+classes.join(" ")+") was not found in a dict of known container items."}var item=new type($(this));var value=item.getFieldValues();values.push(value)});return values},getHtmlTemplate:function(selectorString){var selector="."+selectorString+".openassessment_template";var element=$(selector,this.element);if(element.length==0){throw"There is not an element which matches the selector ("+selector+")"}return element.parent().html()}};OpenAssessment.RubricOption=function(element){this.element=element};OpenAssessment.RubricOption.prototype={getFieldValues:function(){var name=$(".openassessment_criterion_option_name",this.element).prop("value");var points=$(".openassessment_criterion_option_points",this.element).prop("value");var explanation=$(".openassessment_criterion_option_explanation",this.element).prop("value");return{name:name,points:points,explanation:explanation}}};OpenAssessment.RubricCriterion=function(element){this.element=element;this.optionContainer=new OpenAssessment.Container($(".openassessment_criterion_option_list"),{openassessment_criterion_option:OpenAssessment.RubricOption})};OpenAssessment.RubricCriterion.prototype={getFieldValues:function(){var name=$(".openassessment_criterion_name",this.element).prop("value");var prompt=$(".openassessment_criterion_prompt",this.element).prop("value");var feedback=$(".openassessment_criterion_feedback",this.element).prop("value");var options=this.optionContainer.getItemValues();return{name:name,prompt:prompt,options:options,feedback:feedback}}};OpenAssessment.StudioView=function(runtime,element,server){this.runtime=runtime;this.server=server;this.promptView=new OpenAssessment.EditPromptView($("#oa_prompt_editor_wrapper",this.element).get(0));this.settingsView=new OpenAssessment.EditSettingsView($("#oa_basic_settings_editor",this.element).get(0),[new OpenAssessment.EditPeerAssessmentView($("#oa_peer_assessment_editor",this.element).get(0)),new OpenAssessment.EditSelfAssessmentView($("#oa_self_assessment_editor",this.element).get(0)),new OpenAssessment.EditStudentTrainingView($("#oa_student_training_editor",this.element).get(0)),new OpenAssessment.EditExampleBasedAssessmentView($("#oa_ai_assessment_editor",this.element).get(0))]);this.liveElement=$(element);var liveElement=this.liveElement;var criterionHtml=$(".openassessment_criterion",liveElement).parent().html();this.criterionHtmlTemplate=criterionHtml.replace(new RegExp("1","g"),"C-C-C");var optionHtml=$(".openassessment_criterion_option",liveElement).parent().html();var criteriaReplaced=optionHtml.replace(new RegExp("criterion_1","g"),"criterion_C-C-C");this.optionHtmlTemplate=criteriaReplaced.replace(new RegExp("option_1","g"),"option_O-O-O");this.numberOfCriteria=0;this.numberOfOptions=[];this.rubricCriteriaSelectors=[];this.rubricFeedbackPrompt=$("#openassessment_rubric_feedback",liveElement);$("#openassessment_criterion_list",liveElement).empty();this.addNewCriterionToRubric();var view=this;$(".openassessment_save_button",this.element).click(function(eventData){view.save()});$(".openassessment_cancel_button",this.element).click(function(eventData){view.cancel()});$(".openassessment_editor_content_and_tabs",this.element).tabs();$("#openassessment_rubric_add_criterion",this.element).click(function(eventData){view.addNewCriterionToRubric(liveElement)})};OpenAssessment.StudioView.prototype={save:function(){var view=this;this.server.checkReleased().done(function(isReleased){if(isReleased){view.confirmPostReleaseUpdate($.proxy(view.updateEditorContext,view))}else{view.updateEditorContext()}}).fail(function(errMsg){view.showError(errMsg)})},confirmPostReleaseUpdate:function(onConfirm){var msg=gettext("This problem has already been released. Any changes will apply only to future assessments.");if(confirm(msg)){onConfirm()}},addNewCriterionToRubric:function(){var view=this;var liveElement=this.liveElement;var newCriterionID=this.numberOfCriteria+1;this.numberOfCriteria+=1;this.numberOfOptions[newCriterionID]=0;var criterionHtml=this.criterionHtmlTemplate.replace(new RegExp("C-C-C","g"),""+newCriterionID);$("#openassessment_criterion_list",liveElement).append(criterionHtml);liveElement=$("#openassessment_criterion_"+newCriterionID);$(".openassessment_criterion_option_list",liveElement).empty();view.rubricCriteriaSelectors[newCriterionID]={criterion:liveElement,name:$(".openassessment_criterion_name",liveElement).first(),prompt:$(".openassessment_criterion_prompt",liveElement).first(),options:[],feedback:"disabled"};$("input:radio[value=disabled]",liveElement).prop("checked",true);view.addNewOptionToCriterion(liveElement,newCriterionID);$("#openassessment_display_criterion_"+newCriterionID,liveElement).change(function(){if(this.checked){$("#openassessment_criterion_body_"+newCriterionID,liveElement).fadeIn()}else{$("#openassessment_criterion_body_"+newCriterionID,liveElement).fadeOut()}});$("#openassessment_criterion_"+newCriterionID+"_remove",liveElement).click(function(eventData){view.removeCriterionFromRubric(newCriterionID)});$("#openassessment_criterion_"+newCriterionID+"_add_option",liveElement).click(function(eventData){view.addNewOptionToCriterion(liveElement,newCriterionID)});$(".openassessment_feedback_remove_button",liveElement).click(function(eventData){$(".openassessment_criterion_feedback_direction",liveElement).fadeOut();$(".openassessment_criterion_feedback_header_open",liveElement).fadeOut();$(".openassessment_criterion_feedback_header_closed",liveElement).fadeIn();$(".openassessment_feedback_remove_button",liveElement).fadeOut();view.rubricCriteriaSelectors[newCriterionID].hasFeedback=false});$(".openassessment_criterion_feedback_header_closed",liveElement).click(function(eventData){$(".openassessment_criterion_feedback_direction",liveElement).fadeIn();$(".openassessment_criterion_feedback_header_open",liveElement).fadeIn();$(".openassessment_criterion_feedback_header_closed",liveElement).fadeOut();$(".openassessment_feedback_remove_button",liveElement).fadeIn();view.rubricCriteriaSelectors[newCriterionID].hasFeedback=true});$(".openassessment_criterion_feedback_header_closed",liveElement).hide()},removeCriterionFromRubric:function(criterionToRemove){var view=this;var numCriteria=view.numberOfCriteria;var selectors=view.rubricCriteriaSelectors;for(var i=criterionToRemove;i<numCriteria;i++){selectors[i].name.prop("value",selectors[i+1].name.prop("value"));selectors[i].prompt.prop("value",selectors[i+1].prompt.prop("value"));selectors[i].feedback=selectors[i+1].feedback;$('input:radio[value="disabled"]',selectors[i].criterion).prop("checked",true);while(view.numberOfOptions[i]<view.numberOfOptions[i+1]){view.addNewOptionToCriterion(selectors[i].criteria,i)}while(view.numberOfOptions[i]>view.numberOfOptions[i+1]){view.removeOptionFromCriterion(selectors[i].criteria,i,1)}var options1=selectors[i].options;var options2=selectors[i+1].options;var numOptions2=view.numberOfOptions[i+1];for(var j=1;j<numOptions2;j++){options1[j].points.prop("value",options2[j].points.prop("value"));options1[j].name.prop("value",options2[j].name.prop("value"));options1[j].explanation.prop("value",options2[j].explanation.prop("value"))}}view.rubricCriteriaSelectors[view.rubricCriteriaSelectors.length-1].criterion.remove();view.rubricCriteriaSelectors=view.rubricCriteriaSelectors.slice(0,numCriteria);view.numberOfOptions=view.numberOfOptions.slice(0,numCriteria);view.numberOfCriteria-=1},addNewOptionToCriterion:function(liveElement,criterionID){var view=this;var newOptionID=this.numberOfOptions[criterionID]+1;this.numberOfOptions[criterionID]+=1;var optionHtml=this.optionHtmlTemplate;optionHtml=optionHtml.replace(new RegExp("C-C-C","g"),""+criterionID);optionHtml=optionHtml.replace(new RegExp("O-O-O","g"),""+newOptionID);$("#openassessment_criterion_"+criterionID+"_options",liveElement).append(optionHtml);liveElement=$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID);view.rubricCriteriaSelectors[criterionID].options[newOptionID]={option:liveElement,points:$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID+"_points",liveElement),name:$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID+"_name",liveElement),explanation:$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID+"_explanation",liveElement)};$("#openassessment_criterion_"+criterionID+"_option_"+newOptionID+"_remove",liveElement).click(function(eventData){view.removeOptionFromCriterion(liveElement,criterionID,newOptionID)})},removeOptionFromCriterion:function(liveElement,criterionID,optionToRemove){var view=this;var numberOfOptions=view.numberOfOptions[criterionID];var optionSelectors=view.rubricCriteriaSelectors[criterionID].options;for(var i=optionToRemove;i<numberOfOptions;i++){optionSelectors[i].points.prop("value",optionSelectors[i+1].points.prop("value"));optionSelectors[i].name.prop("value",optionSelectors[i+1].name.prop("value"));optionSelectors[i].explanation.prop("value",optionSelectors[i+1].explanation.prop("value"))}optionSelectors[optionSelectors.length-1].option.remove();view.rubricCriteriaSelectors[criterionID].options=view.rubricCriteriaSelectors[criterionID].options.slice(0,optionSelectors.length-1);view.numberOfOptions[criterionID]-=1},updateEditorContext:function(){this.runtime.notify("save",{state:"start"});var rubricCriteria=[];for(var i=1;i<=this.numberOfCriteria;i++){var selectorDict=this.rubricCriteriaSelectors[i];var criterionValueDict={order_num:i-1,name:selectorDict.name.prop("value"),prompt:selectorDict.prompt.prop("value"),feedback:$("#openassessment_criterion_"+i+"_feedback").val()};var optionSelectorList=selectorDict.options;var optionValueList=[];for(var j=1;j<=this.numberOfOptions[i];j++){var optionSelectors=optionSelectorList[j];optionValueList=optionValueList.concat([{order_num:j-1,points:parseInt(optionSelectors.points.val(),10),name:optionSelectors.name.val(),explanation:optionSelectors.explanation.val()}])}criterionValueDict.options=optionValueList;rubricCriteria=rubricCriteria.concat([criterionValueDict])}var view=this;this.server.updateEditorContext({title:view.settingsView.displayName(),prompt:view.promptView.promptText(),feedbackPrompt:this.rubricFeedbackPrompt.val(),submissionStart:view.settingsView.submissionStart(),submissionDue:view.settingsView.submissionDue(),criteria:rubricCriteria,assessments:view.settingsView.assessmentsDescription()}).done(function(){view.runtime.notify("save",{state:"end"})}).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){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.StudioView(runtime,element,server)}OpenAssessment.ToggleControl=function(element,hiddenSelector,shownSelector){this.element=element;this.hiddenSelector=hiddenSelector;this.shownSelector=shownSelector};OpenAssessment.ToggleControl.prototype={install:function(checkboxSelector){$(checkboxSelector,this.element).change(this,function(event){var control=event.data;if(this.checked){control.show()}else{control.hide()}});return this},show:function(){$(this.hiddenSelector,this.element).fadeOut("fast");$(this.shownSelector,this.element).fadeIn()},hide:function(){$(this.hiddenSelector,this.element).fadeIn();$(this.shownSelector,this.element).fadeOut()}};OpenAssessment.EditPeerAssessmentView=function(element){this.element=element;this.name="peer-assessment";new OpenAssessment.ToggleControl(this.element,"#peer_assessment_description_closed","#peer_assessment_settings_editor").install("#include_peer_assessment")};OpenAssessment.EditPeerAssessmentView.prototype={description:function(){return{must_grade:this.mustGradeNum(),must_be_graded_by:this.mustBeGradedByNum(),start:this.startDatetime(),due:this.dueDatetime()}},isEnabled:function(isEnabled){var sel=$("#include_peer_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},mustGradeNum:function(num){var sel=$("#peer_assessment_must_grade",this.element);return OpenAssessment.Fields.intField(sel,num)},mustBeGradedByNum:function(num){var sel=$("#peer_assessment_graded_by",this.element);return OpenAssessment.Fields.intField(sel,num)},startDatetime:function(datetime){var sel=$("#peer_assessment_start_date",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)},dueDatetime:function(datetime){var sel=$("#peer_assessment_due_date",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)}};OpenAssessment.EditSelfAssessmentView=function(element){this.element=element;this.name="self-assessment";new OpenAssessment.ToggleControl(this.element,"#self_assessment_description_closed","#self_assessment_settings_editor").install("#include_self_assessment")};OpenAssessment.EditSelfAssessmentView.prototype={description:function(){return{start:this.startDatetime(),due:this.dueDatetime()}},isEnabled:function(isEnabled){var sel=$("#include_self_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},startDatetime:function(datetime){var sel=$("#self_assessment_start_date",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)},dueDatetime:function(datetime){var sel=$("#self_assessment_due_date",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)}};OpenAssessment.EditStudentTrainingView=function(element){this.element=element;this.name="student-training";new OpenAssessment.ToggleControl(this.element,"#student_training_description_closed","#student_training_settings_editor").install("#include_student_training")};OpenAssessment.EditStudentTrainingView.prototype={description:function(){return{examples:this.exampleDefinitions()}},isEnabled:function(isEnabled){var sel=$("#include_student_training",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},exampleDefinitions:function(xml){var sel=$("#student_training_examples",this.element);return OpenAssessment.Fields.stringField(sel,xml)}};OpenAssessment.EditExampleBasedAssessmentView=function(element){this.element=element;this.name="example-based-assessment";new OpenAssessment.ToggleControl(this.element,"#ai_assessment_description_closed","#ai_assessment_settings_editor").install("#include_ai_assessment")};OpenAssessment.EditExampleBasedAssessmentView.prototype={description:function(){return{examples:this.exampleDefinitions()}},isEnabled:function(isEnabled){var sel=$("#include_ai_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},exampleDefinitions:function(xml){var sel=$("#ai_training_examples",this.element);return OpenAssessment.Fields.stringField(sel,xml)}};OpenAssessment.Fields={stringField:function(sel,value){if(typeof value!=="undefined"){sel.val(value)}return sel.val()},datetimeField:function(sel,value){if(typeof value!=="undefined"){sel.val(value)}var fieldValue=sel.val();return fieldValue!==""?fieldValue:null},intField:function(sel,value){if(typeof value!=="undefined"){sel.val(value)}return parseInt(sel.val(),10)},booleanField:function(sel,value){if(typeof value!=="undefined"){sel.prop("checked",value)}return sel.prop("checked")}};OpenAssessment.EditPromptView=function(element){this.element=element};OpenAssessment.EditPromptView.prototype={promptText:function(text){var sel=$("#openassessment_prompt_editor",this.element);return OpenAssessment.Fields.stringField(sel,text)}};OpenAssessment.EditRubricView=function(element){this.element=element};OpenAssessment.EditRubricView.prototype={load:function(){},criteriaDefinition:function(){}};OpenAssessment.EditSettingsView=function(element,assessmentViews){this.element=element;this.assessmentViews=assessmentViews};OpenAssessment.EditSettingsView.prototype={displayName:function(name){var sel=$("#openassessment_title_editor",this.element);return OpenAssessment.Fields.stringField(sel,name)},submissionStart:function(datetime){var sel=$("#openassessment_submission_start_editor",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)},submissionDue:function(datetime){var sel=$("#openassessment_submission_start_editor",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)},imageSubmissionEnabled:function(isEnabled){var sel=$("#openassessment_submission_image_editor",this.element);if(typeof isEnabled!=="undefined"){if(isEnabled){sel.val(1)}else{sel.val(0)}}return sel.val()==1},assessmentsDescription:function(){assessmentDescList=[];for(var idx in this.assessmentViews){var asmntView=this.assessmentViews[idx];if(asmntView.isEnabled()){var description=asmntView.description();description["name"]=asmntView.name;assessmentDescList.push(description)}}return assessmentDescList}};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.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,index){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(sel,hidden){sel.toggleClass("is--hidden",hidden);sel.attr("aria-hidden",hidden?"true":"false")},isHidden:function(sel){return sel.hasClass("is--hidden")&&sel.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.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)}).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.installHandlers(false)}).fail(function(errMsg){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.installHandlers(true)}).fail(function(errMsg){view.baseView.showLoadError("peer-assessment")})},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel,$.proxy(view.loadContinuedAssessment,view));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()}})},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)}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){view.load();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;view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.overallFeedback()).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})},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)}}};OpenAssessment.ResponseView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.savedResponse="";this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.installHandlers();view.setAutoSaveEnabled(true)}).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.handleResponseChanged()};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()})},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)}},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{var label=gettext("Status of Your Response");sel.html('<span class="sr">'+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(text){var sel=$("#submission__answer__value",this.element);if(typeof text==="undefined"){return sel.val()}else{sel.val(text)}},responseChanged:function(){var currentResponse=$.trim(this.response());var savedResponse=$.trim(this.savedResponse);return savedResponse!==currentResponse},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isBlank=$.trim(this.response())!=="";this.submitEnabled(isBlank);if(this.responseChanged()){this.saveEnabled(isBlank);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();view.submitEnabled(currentResponse!=="");if(currentResponse==savedResponse){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;this.confirmSubmission().pipe(function(){var submission=$("#submission__answer__value",view.element).val();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="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."; if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}OpenAssessment.BaseView=function(runtime,element,server){this.runtime=runtime;this.element=element;this.server=server;this.responseView=new OpenAssessment.ResponseView(this.element,this.server,this);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.gradeView=new OpenAssessment.GradeView(this.element,this.server,this);this.messageView=new OpenAssessment.MessageView(this.element,this.server,this);this.staffInfoView=new OpenAssessment.StaffInfoView(this.element,this.server,this)};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.load();this.loadAssessmentModules();this.staffInfoView.load()},loadAssessmentModules:function(){this.trainingView.load();this.peerView.load();this.selfView.load();this.gradeView.load()},loadMessageView:function(){this.messageView.load()},toggleActionError:function(type,msg){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"}if(container===null){if(msg!==null){console.log(msg)}}else{var msgHtml=msg===null?"":msg;$(container+" .message__content",element).html("<p>"+msgHtml+"</p>");$(container,element).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(gettext("Unable to Load"))}};function OpenAssessmentBlock(runtime,element){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.BaseView(runtime,element,server);view.load()}OpenAssessment.Container=function(containerItem,kwargs){this.containerItem=containerItem;this.containerElement=kwargs.containerElement;this.templateElement=kwargs.templateElement;this.addButtonElement=kwargs.addButtonElement;this.removeButtonClass=kwargs.removeButtonClass;this.containerItemClass=kwargs.containerItemClass;$(this.addButtonElement).click($.proxy(this.add,this));var container=this;$("."+this.removeButtonClass,this.containerElement).click(function(eventData){container.remove(eventData.target)});$("."+this.containerItemClass,this.containerElement).each(function(index,element){new container.containerItem(element)})};OpenAssessment.Container.prototype={add:function(){$(this.templateElement).clone().removeAttr("id").toggleClass("is--hidden",false).toggleClass(this.containerItemClass,true).appendTo($(this.containerElement));var container=this;var containerItem=$("."+this.containerItemClass,this.containerElement).last();containerItem.find("."+this.removeButtonClass).click(function(eventData){container.remove(eventData.target)});new this.containerItem(containerItem.get(0))},remove:function(element){$(element).closest("."+this.containerItemClass).remove()},getItemValues:function(){var values=[];var container=this;$("."+this.containerItemClass,this.containerElement).each(function(index,element){var containerItem=new container.containerItem(element);var fieldValues=containerItem.getFieldValues();values.push(fieldValues)});return values},getItemElement:function(index){var element=$("."+this.containerItemClass,this.containerElement).get(index);return element!==undefined?element:null}};OpenAssessment.RubricOption=function(element){this.element=element};OpenAssessment.RubricOption.prototype={getFieldValues:function(){return{name:OpenAssessment.Fields.stringField($(".openassessment_criterion_option_name",this.element)),points:OpenAssessment.Fields.intField($(".openassessment_criterion_option_points",this.element)),explanation:OpenAssessment.Fields.stringField($(".openassessment_criterion_option_explanation",this.element))}}};OpenAssessment.RubricCriterion=function(element){this.element=element;this.optionContainer=new OpenAssessment.Container(OpenAssessment.RubricOption,{containerElement:$(".openassessment_criterion_option_list",this.element).get(0),templateElement:$("#openassessment_option_template").get(0),addButtonElement:$(".openassessment_criterion_add_option",this.element).get(0),removeButtonClass:"openassessment_rubric_remove_button",containerItemClass:"openassessment_criterion_option"})};OpenAssessment.RubricCriterion.prototype={getFieldValues:function(){return{name:OpenAssessment.Fields.stringField($(".openassessment_criterion_name",this.element)),prompt:OpenAssessment.Fields.stringField($(".openassessment_criterion_prompt",this.element)),feedback:OpenAssessment.Fields.stringField($(".openassessment_criterion_feedback",this.element)),options:this.optionContainer.getItemValues()}}};OpenAssessment.StudioView=function(runtime,element,server){this.runtime=runtime;this.server=server;$(".openassessment_editor_content_and_tabs",this.element).tabs();this.promptView=new OpenAssessment.EditPromptView($("#oa_prompt_editor_wrapper",this.element).get(0));this.settingsView=new OpenAssessment.EditSettingsView($("#oa_basic_settings_editor",this.element).get(0),[new OpenAssessment.EditPeerAssessmentView($("#oa_peer_assessment_editor",this.element).get(0)),new OpenAssessment.EditSelfAssessmentView($("#oa_self_assessment_editor",this.element).get(0)),new OpenAssessment.EditStudentTrainingView($("#oa_student_training_editor",this.element).get(0)),new OpenAssessment.EditExampleBasedAssessmentView($("#oa_ai_assessment_editor",this.element).get(0))]);this.rubricView=new OpenAssessment.EditRubricView($("#oa_rubric_editor_wrapper",this.element).get(0));$(".openassessment_save_button",this.element).click($.proxy(this.save,this));$(".openassessment_cancel_button",this.element).click($.proxy(this.cancel,this))};OpenAssessment.StudioView.prototype={save:function(){var view=this;this.server.checkReleased().done(function(isReleased){if(isReleased){view.confirmPostReleaseUpdate($.proxy(view.updateEditorContext,view))}else{view.updateEditorContext()}}).fail(function(errMsg){view.showError(errMsg)})},confirmPostReleaseUpdate:function(onConfirm){var msg=gettext("This problem has already been released. Any changes will apply only to future assessments.");if(confirm(msg)){onConfirm()}},updateEditorContext:function(){this.runtime.notify("save",{state:"start"});var view=this;this.server.updateEditorContext({prompt:view.promptView.promptText(),feedbackPrompt:view.rubricView.feedbackPrompt(),criteria:view.rubricView.criteriaDefinition(),title:view.settingsView.displayName(),submissionStart:view.settingsView.submissionStart(),submissionDue:view.settingsView.submissionDue(),assessments:view.settingsView.assessmentsDescription()}).done(function(){view.runtime.notify("save",{state:"end"})}).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){var server=new OpenAssessment.Server(runtime,element);var view=new OpenAssessment.StudioView(runtime,element,server)}OpenAssessment.ToggleControl=function(element,hiddenSelector,shownSelector){this.element=element;this.hiddenSelector=hiddenSelector;this.shownSelector=shownSelector};OpenAssessment.ToggleControl.prototype={install:function(checkboxSelector){$(checkboxSelector,this.element).change(this,function(event){var control=event.data;if(this.checked){control.show()}else{control.hide()}});return this},show:function(){$(this.hiddenSelector,this.element).fadeOut("fast");$(this.shownSelector,this.element).fadeIn()},hide:function(){$(this.hiddenSelector,this.element).fadeIn();$(this.shownSelector,this.element).fadeOut()}};OpenAssessment.EditPeerAssessmentView=function(element){this.element=element;this.name="peer-assessment";new OpenAssessment.ToggleControl(this.element,"#peer_assessment_description_closed","#peer_assessment_settings_editor").install("#include_peer_assessment")};OpenAssessment.EditPeerAssessmentView.prototype={description:function(){return{must_grade:this.mustGradeNum(),must_be_graded_by:this.mustBeGradedByNum(),start:this.startDatetime(),due:this.dueDatetime()}},isEnabled:function(isEnabled){var sel=$("#include_peer_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},mustGradeNum:function(num){var sel=$("#peer_assessment_must_grade",this.element);return OpenAssessment.Fields.intField(sel,num)},mustBeGradedByNum:function(num){var sel=$("#peer_assessment_graded_by",this.element);return OpenAssessment.Fields.intField(sel,num)},startDatetime:function(datetime){var sel=$("#peer_assessment_start_date",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)},dueDatetime:function(datetime){var sel=$("#peer_assessment_due_date",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)}};OpenAssessment.EditSelfAssessmentView=function(element){this.element=element;this.name="self-assessment";new OpenAssessment.ToggleControl(this.element,"#self_assessment_description_closed","#self_assessment_settings_editor").install("#include_self_assessment")};OpenAssessment.EditSelfAssessmentView.prototype={description:function(){return{start:this.startDatetime(),due:this.dueDatetime()}},isEnabled:function(isEnabled){var sel=$("#include_self_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},startDatetime:function(datetime){var sel=$("#self_assessment_start_date",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)},dueDatetime:function(datetime){var sel=$("#self_assessment_due_date",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)}};OpenAssessment.EditStudentTrainingView=function(element){this.element=element;this.name="student-training";new OpenAssessment.ToggleControl(this.element,"#student_training_description_closed","#student_training_settings_editor").install("#include_student_training")};OpenAssessment.EditStudentTrainingView.prototype={description:function(){return{examples:this.exampleDefinitions()}},isEnabled:function(isEnabled){var sel=$("#include_student_training",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},exampleDefinitions:function(xml){var sel=$("#student_training_examples",this.element);return OpenAssessment.Fields.stringField(sel,xml)}};OpenAssessment.EditExampleBasedAssessmentView=function(element){this.element=element;this.name="example-based-assessment";new OpenAssessment.ToggleControl(this.element,"#ai_assessment_description_closed","#ai_assessment_settings_editor").install("#include_ai_assessment")};OpenAssessment.EditExampleBasedAssessmentView.prototype={description:function(){return{examples:this.exampleDefinitions()}},isEnabled:function(isEnabled){var sel=$("#include_ai_assessment",this.element);return OpenAssessment.Fields.booleanField(sel,isEnabled)},exampleDefinitions:function(xml){var sel=$("#ai_training_examples",this.element);return OpenAssessment.Fields.stringField(sel,xml)}};OpenAssessment.Fields={stringField:function(sel,value){if(typeof value!=="undefined"){sel.val(value)}return sel.val()},datetimeField:function(sel,value){if(typeof value!=="undefined"){sel.val(value)}var fieldValue=sel.val();return fieldValue!==""?fieldValue:null},intField:function(sel,value){if(typeof value!=="undefined"){sel.val(value)}return parseInt(sel.val(),10)},booleanField:function(sel,value){if(typeof value!=="undefined"){sel.prop("checked",value)}return sel.prop("checked")}};OpenAssessment.EditPromptView=function(element){this.element=element};OpenAssessment.EditPromptView.prototype={promptText:function(text){var sel=$("#openassessment_prompt_editor",this.element);return OpenAssessment.Fields.stringField(sel,text)}};OpenAssessment.EditRubricView=function(element){this.element=element;this.criteriaContainer=new OpenAssessment.Container(OpenAssessment.RubricCriterion,{containerElement:$("#openassessment_criterion_list",this.element).get(0),templateElement:$("#openassessment_criterion_template",this.element).get(0),addButtonElement:$("#openassessment_rubric_add_criterion",this.element).get(0),removeButtonClass:"openassessment_rubric_remove_button",containerItemClass:"openassessment_criterion"})};OpenAssessment.EditRubricView.prototype={criteriaDefinition:function(){var criteria=this.criteriaContainer.getItemValues();for(var criterion_idx=0;criterion_idx<criteria.length;criterion_idx++){var criterion=criteria[criterion_idx];criterion.order_num=criterion_idx;for(var option_idx=0;option_idx<criterion.options.length;option_idx++){var option=criterion.options[option_idx];option.order_num=option_idx}}return criteria},feedbackPrompt:function(text){var sel=$("#openassessment_rubric_feedback",this.element);return OpenAssessment.Fields.stringField(sel,text)}};OpenAssessment.EditSettingsView=function(element,assessmentViews){this.element=element;this.assessmentViews=assessmentViews};OpenAssessment.EditSettingsView.prototype={displayName:function(name){var sel=$("#openassessment_title_editor",this.element);return OpenAssessment.Fields.stringField(sel,name)},submissionStart:function(datetime){var sel=$("#openassessment_submission_start_editor",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)},submissionDue:function(datetime){var sel=$("#openassessment_submission_start_editor",this.element);return OpenAssessment.Fields.datetimeField(sel,datetime)},imageSubmissionEnabled:function(isEnabled){var sel=$("#openassessment_submission_image_editor",this.element);if(typeof isEnabled!=="undefined"){if(isEnabled){sel.val(1)}else{sel.val(0)}}return sel.val()==1},assessmentsDescription:function(){assessmentDescList=[];for(var idx in this.assessmentViews){var asmntView=this.assessmentViews[idx];if(asmntView.isEnabled()){var description=asmntView.description();description["name"]=asmntView.name;assessmentDescList.push(description)}}return assessmentDescList}};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.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,index){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(sel,hidden){sel.toggleClass("is--hidden",hidden);sel.attr("aria-hidden",hidden?"true":"false")},isHidden:function(sel){return sel.hasClass("is--hidden")&&sel.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.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)}).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.installHandlers(false)}).fail(function(errMsg){view.baseView.showLoadError("peer-assessment")});view.baseView.loadMessageView()},loadContinuedAssessment:function(){var view=this;this.server.renderContinuedPeer().done(function(html){$("#openassessment__peer-assessment",view.element).replaceWith(html);view.installHandlers(true)}).fail(function(errMsg){view.baseView.showLoadError("peer-assessment")})},installHandlers:function(isContinuedAssessment){var sel=$("#openassessment__peer-assessment",this.element);var view=this;this.baseView.setUpCollapseExpand(sel,$.proxy(view.loadContinuedAssessment,view));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()}})},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)}},peerAssess:function(){var view=this;var baseView=view.baseView;this.peerAssessRequest(function(){view.load();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;view.baseView.toggleActionError("peer",null);view.peerSubmitEnabled(false);this.server.peerAssess(this.rubric.optionsSelected(),this.rubric.criterionFeedback(),this.overallFeedback()).done(successFunction).fail(function(errMsg){view.baseView.toggleActionError("peer",errMsg);view.peerSubmitEnabled(true)})},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)}}};OpenAssessment.ResponseView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView;this.savedResponse="";this.lastChangeTime=Date.now();this.errorOnLastSave=false;this.autoSaveTimerId=null};OpenAssessment.ResponseView.prototype={AUTO_SAVE_POLL_INTERVAL:2e3,AUTO_SAVE_WAIT:3e4,load:function(){var view=this;this.server.render("submission").done(function(html){$("#openassessment__response",view.element).replaceWith(html);view.installHandlers();view.setAutoSaveEnabled(true)}).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.handleResponseChanged()};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()})},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)}},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{var label=gettext("Status of Your Response");sel.html('<span class="sr">'+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(text){var sel=$("#submission__answer__value",this.element);if(typeof text==="undefined"){return sel.val()}else{sel.val(text)}},responseChanged:function(){var currentResponse=$.trim(this.response());var savedResponse=$.trim(this.savedResponse);return savedResponse!==currentResponse},autoSave:function(){var timeSinceLastChange=Date.now()-this.lastChangeTime;if(this.responseChanged()&&timeSinceLastChange>this.AUTO_SAVE_WAIT&&!this.errorOnLastSave){this.save()}},handleResponseChanged:function(){var isBlank=$.trim(this.response())!=="";this.submitEnabled(isBlank);if(this.responseChanged()){this.saveEnabled(isBlank);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();view.submitEnabled(currentResponse!=="");if(currentResponse==savedResponse){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;this.confirmSubmission().pipe(function(){var submission=$("#submission__answer__value",view.element).val();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="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()}})}};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},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){$(this.element).change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;var canSubmit=numChecked==numAvailable;callback(canSubmit)})},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.installHandlers()}).fail(function(errMsg){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)}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)}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);var options=this.rubric.optionsSelected();this.server.selfAssess(options).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},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,[gettext("This section could not be loaded.")])})}).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,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(student_id){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{student_id:student_id}}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,[gettext("This section 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})}).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",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})}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){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}).done(function(data){if(data.success){defer.resolve()
return $.Deferred(function(defer){if(confirm(msg)){defer.resolve()}else{defer.reject()}})}};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},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){$(this.element).change(function(){var numChecked=$("input[type=radio]:checked",this).length;var numAvailable=$(".field--radio.assessment__rubric__question",this).length;var canSubmit=numChecked==numAvailable;callback(canSubmit)})},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.installHandlers()}).fail(function(errMsg){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)}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)}},selfAssess:function(){var view=this;var baseView=this.baseView;baseView.toggleActionError("self",null);view.selfSubmitEnabled(false);var options=this.rubric.optionsSelected();this.server.selfAssess(options).done(function(){baseView.loadAssessmentModules();baseView.scrollToTop()}).fail(function(errMsg){baseView.toggleActionError("self",errMsg);view.selfSubmitEnabled(true)})}};OpenAssessment.Server=function(runtime,element){this.runtime=runtime;this.element=element};OpenAssessment.Server.prototype={url:function(handler){return this.runtime.handlerUrl(this.element,handler)},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,[gettext("This section could not be loaded.")])})}).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,[gettext("This section could not be loaded.")])})}).promise()},studentInfo:function(student_id){var url=this.url("render_student_info");return $.Deferred(function(defer){$.ajax({url:url,type:"POST",dataType:"html",data:{student_id:student_id}}).done(function(data){defer.resolveWith(this,[data])}).fail(function(data){defer.rejectWith(this,[gettext("This section 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})}).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",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})}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){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}).done(function(data){if(data.success){defer.resolve()}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback){var url=this.url("peer_assess");var payload=JSON.stringify({options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback});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,[gettext("This assessment could not be submitted.")])})}).promise()},selfAssess:function(optionsSelected){var url=this.url("self_assess");var payload=JSON.stringify({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,[gettext("This assessment could not be submitted.")])})})},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}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){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:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){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:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(kwargs){var url=this.url("update_editor_context");var payload=JSON.stringify({prompt:kwargs.prompt,feedback_prompt:kwargs.feedbackPrompt,title:kwargs.title,submission_start:kwargs.submissionStart,submission_due:kwargs.submissionDue,criteria:kwargs.criteria,assessments:kwargs.assessments});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,[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}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()}};if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}OpenAssessment.StaffInfoView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffInfoView.prototype={load:function(){var view=this;if($("#openassessment__staff-info",view.element).length>0){this.server.render("staff_info").done(function(html){$("#openassessment__staff-info",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){view.baseView.showLoadError("staff_info")})}},loadStudentInfo:function(){var view=this;var sel=$("#openassessment__staff-info",this.element);var student_id=sel.find("#openassessment__student_id").val();this.server.studentInfo(student_id).done(function(html){$("#openassessment__student-info",view.element).replaceWith(html)}).fail(function(errMsg){view.showLoadError("student_info")})},installHandlers:function(){var sel=$("#openassessment__staff-info",this.element);var view=this;if(sel.length<=0){return}this.baseView.setUpCollapseExpand(sel,function(){});sel.find("#openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});sel.find("#submit_student_id").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});sel.find("#schedule_training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});sel.find("#reschedule_unfinished_tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$("#schedule_training_message",this.element).text(msg)}).fail(function(errMsg){$("#schedule_training_message",this.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$("#reschedule_unfinished_tasks_message",this.element).text(msg)}).fail(function(errMsg){$("#reschedule_unfinished_tasks_message",this.element).text(errMsg)})}};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.installHandlers()}).fail(function(errMsg){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",this.element);var instructions=$("#openassessment__student-training--instructions",this.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)}}}; }else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("This feedback could not be submitted.")])})}).promise()},peerAssess:function(optionsSelected,criterionFeedback,overallFeedback){var url=this.url("peer_assess");var payload=JSON.stringify({options_selected:optionsSelected,criterion_feedback:criterionFeedback,overall_feedback:overallFeedback});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,[gettext("This assessment could not be submitted.")])})}).promise()},selfAssess:function(optionsSelected){var url=this.url("self_assess");var payload=JSON.stringify({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,[gettext("This assessment could not be submitted.")])})})},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}).done(function(data){if(data.success){defer.resolveWith(this,[data.corrections])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){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:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){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:'""'}).done(function(data){if(data.success){defer.resolveWith(this,[data.msg])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("One or more rescheduling tasks failed.")])})})},updateEditorContext:function(kwargs){var url=this.url("update_editor_context");var payload=JSON.stringify({prompt:kwargs.prompt,feedback_prompt:kwargs.feedbackPrompt,title:kwargs.title,submission_start:kwargs.submissionStart,submission_due:kwargs.submissionDue,criteria:kwargs.criteria,assessments:kwargs.assessments});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,[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}).done(function(data){if(data.success){defer.resolveWith(this,[data.is_released])}else{defer.rejectWith(this,[data.msg])}}).fail(function(data){defer.rejectWith(this,[gettext("The server could not be contacted.")])})}).promise()}};if(typeof OpenAssessment=="undefined"||!OpenAssessment){OpenAssessment={}}if(typeof window.gettext==="undefined"){window.gettext=function(text){return text}}OpenAssessment.StaffInfoView=function(element,server,baseView){this.element=element;this.server=server;this.baseView=baseView};OpenAssessment.StaffInfoView.prototype={load:function(){var view=this;if($("#openassessment__staff-info",view.element).length>0){this.server.render("staff_info").done(function(html){$("#openassessment__staff-info",view.element).replaceWith(html);view.installHandlers()}).fail(function(errMsg){view.baseView.showLoadError("staff_info")})}},loadStudentInfo:function(){var view=this;var sel=$("#openassessment__staff-info",this.element);var student_id=sel.find("#openassessment__student_id").val();this.server.studentInfo(student_id).done(function(html){$("#openassessment__student-info",view.element).replaceWith(html)}).fail(function(errMsg){view.showLoadError("student_info")})},installHandlers:function(){var sel=$("#openassessment__staff-info",this.element);var view=this;if(sel.length<=0){return}this.baseView.setUpCollapseExpand(sel,function(){});sel.find("#openassessment_student_info_form").submit(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});sel.find("#submit_student_id").click(function(eventObject){eventObject.preventDefault();view.loadStudentInfo()});sel.find("#schedule_training").click(function(eventObject){eventObject.preventDefault();view.scheduleTraining()});sel.find("#reschedule_unfinished_tasks").click(function(eventObject){eventObject.preventDefault();view.rescheduleUnfinishedTasks()})},scheduleTraining:function(){var view=this;this.server.scheduleTraining().done(function(msg){$("#schedule_training_message",this.element).text(msg)}).fail(function(errMsg){$("#schedule_training_message",this.element).text(errMsg)})},rescheduleUnfinishedTasks:function(){var view=this;this.server.rescheduleUnfinishedTasks().done(function(msg){$("#reschedule_unfinished_tasks_message",this.element).text(msg)}).fail(function(errMsg){$("#reschedule_unfinished_tasks_message",this.element).text(errMsg)})}};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.installHandlers()}).fail(function(errMsg){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",this.element);var instructions=$("#openassessment__student-training--instructions",this.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
...@@ -4,80 +4,158 @@ ...@@ -4,80 +4,158 @@
describe("OpenAssessment.Container", function () { describe("OpenAssessment.Container", function () {
var counter = 0;
var StubContainerItem = function(element) {
// Assign an ID to the item if it doesn't already have one.
if ($(element).attr("test_id") === "") {
$(element).attr("test_id", counter);
counter += 1;
}
this.getFieldValues = function() {
var testIdNum = parseInt($(element).attr("test_id"), 10);
return { id: testIdNum };
};
};
var container = null; var container = null;
var createContainer = function() {
return new OpenAssessment.Container(
StubContainerItem, {
containerElement: $("#container").get(0),
templateElement: $("#template").get(0),
addButtonElement: $("#add_button").get(0),
removeButtonClass: "remove_button",
containerItemClass: "test_item",
}
);
};
beforeEach(function () { beforeEach(function () {
jasmine.getFixtures().fixturesPath = 'base/fixtures'; // Reset the counter before each test
loadFixtures('oa_edit.html'); counter = 0;
container = new OpenAssessment.Container( // Install a minimal fixture
$('#openassessment_criterion_list'), // We don't need to use a full ORA2 template for this,
{ // so we just define the fixture inline.
'openassessment_criterion': OpenAssessment.RubricCriterion setFixtures(
} '<div id="container" />' +
) '<div id="template" test_id="">' +
}); '<div class="remove_button" />' +
'</div>' +
'<div id="add_button" />'
);
it("adds a criterion", function () { // Create the container and configure it
var previousSize = $('.openassessment_criterion').length; // to use the stub container item.
container.add('openassessment_criterion'); container = createContainer();
var newSize = $('.openassessment_criterion').length;
expect(newSize).toEqual(previousSize + 1);
}); });
it("removes a criterion", function () { it("adds and removes items", function() {
container.add('openassessment_criterion'); // Initially, there should be no items
var previousSize = $('.openassessment_criterion').length; expect(container.getItemValues()).toEqual([]);
container.remove(1);
var newSize = $('.openassessment_criterion').length; // Add an item
expect(newSize).toEqual(previousSize - 1); container.add();
expect(container.getItemValues()).toEqual([
{ id: 0 }
]);
// Add a second item
container.add();
expect(container.getItemValues()).toEqual([
{ id: 0 },
{ id: 1 }
]);
// Add a third item
container.add();
expect(container.getItemValues()).toEqual([
{ id: 0 },
{ id: 1 },
{ id: 2 }
]);
// Remove the second item
container.remove(container.getItemElement(1));
expect(container.getItemValues()).toEqual([
{ id: 0 },
{ id: 2 },
]);
// Remove the first item
container.remove(container.getItemElement(0));
expect(container.getItemValues()).toEqual([
{ id: 2 },
]);
// Remove the last item
container.remove(container.getItemElement(0));
expect(container.getItemValues()).toEqual([]);
}); });
it("requests an invalid template", function () { it("ignores unrecognized DOM elements", function() {
var error = false; // Add some items to the container
try{ container.add();
container.getHtmlTemplate('not_a_template'); container.add();
} catch (e) { expect(container.getItemValues().length).toEqual(2);
error = true;
} // Add an extra element to the container in the DOM
expect(error).toBe(true); $("<p>Not a container item!</p>").appendTo("#parent_element");
// Expect the count to remain the same
expect(container.getItemValues().length).toEqual(2);
// Add another element
container.add();
expect(container.getItemValues().length).toEqual(3);
// Remove the first element
container.remove(container.getItemElement(0));
expect(container.getItemValues().length).toEqual(2);
}); });
it("installs delete buttons", function () { it("adds an element when the add button is pressed", function() {
container.installDeleteButtons($('#openassessment_criterion_list')); // Press the add button
expect(container.getItemValues().length).toEqual(0);
$("#add_button").click();
expect(container.getItemValues().length).toEqual(1);
}); });
it("parses out unacceptable container items", function () { it("removes an element when the remove button is pressed", function() {
container.element.append("<p> Hello, not an element here. </p>"); // Add some items
var error = false; container.add();
try{ container.add();
container.getItemValues(); container.add();
} catch (e) { expect(container.getItemValues().length).toEqual(3);
error = true;
} // Press the button to delete the second item
expect(error).toBe(true); $(".remove_button", container.getItemElement(1)).click();
expect(container.getItemValues().length).toEqual(2);
expect(container.getItemValues()).toEqual([
{ id: 0 },
{ id: 2 }
]);
}); });
it("returns correct item dictionary", function () { it("configures remove buttons for pre-existing items", function() {
var items = container.getItemValues(); // Add an item directly to the container element in the DOM,
var item = items[0]; // before initializing the container object.
$("#container").append(
'<div class="test_item" test_id="0">' +
'<div class="remove_button" />' +
'<div>'
);
expect(item.name).toEqual(jasmine.any(String)); // Initialize the container object
expect(item.prompt).toEqual(jasmine.any(String)); container = createContainer();
expect(item.feedback).toEqual(jasmine.any(String));
expect(item.options).toEqual(jasmine.any(Array));
expect(item.options[0].name).toEqual(jasmine.any(String));
expect(parseInt(item.options[0].points)).toEqual(jasmine.any(Number));
expect(item.options[0].explanation).toEqual(jasmine.any(String));
}); // Verify that the container recognizes the pre-existing item
expect(container.getItemValues()).toEqual([{ id: 0 }]);
it("checks for undefined selection type", function () { // Expect that we can click the "remove" button
var error = false; // to remove the item.
try{ $(".remove_button", container.getItemElement(0)).click();
container.add("lolz this isn't a valid selection type"); expect(container.getItemValues().length).toEqual(0);
} catch (e) {
error = true;
}
expect(error).toBe(true);
}); });
}); });
\ No newline at end of file
/**
Tests for the rubric editing view.
**/
describe("OpenAssessment.EditRubricView", function() {
var view = null;
beforeEach(function() {
jasmine.getFixtures().fixturesPath = 'base/fixtures';
loadFixtures('oa_edit.html');
var el = $("#oa_rubric_editor_wrapper").get(0);
view = new OpenAssessment.EditRubricView(el);
});
it("reads a criteria definition from the editor", function() {
// This assumes a particular structure of the DOM,
// which is set by the HTML fixture.
var criteria = view.criteriaDefinition();
expect(criteria.length).toEqual(3);
// Criterion with two options, feedback disabled
expect(criteria[0]).toEqual({
name: "Criterion with two options",
prompt: "Prompt for criterion with two options",
order_num: 0,
feedback: "disabled",
options: [
{
order_num: 0,
points: 1,
name: "Fair",
explanation: "Fair explanation"
},
{
order_num: 1,
points: 2,
name: "Good",
explanation: "Good explanation"
}
],
});
// Criterion with no options, feedback required
expect(criteria[1]).toEqual({
name: "Criterion with no options",
prompt: "Prompt for criterion with no options",
order_num: 1,
feedback: "required",
options: []
});
// Criterion with one option, feeback optional
expect(criteria[2]).toEqual({
name: "Criterion with optional feedback",
prompt: "Prompt for criterion with optional feedback",
order_num: 2,
feedback: "optional",
options: [
{
order_num: 0,
points: 2,
name: "Good",
explanation: "Good explanation"
}
]
});
});
it("reads the feedback prompt from the editor", function() {
view.feedbackPrompt("");
expect(view.feedbackPrompt()).toEqual("");
var prompt = "How do you think the student did overall?";
view.feedbackPrompt(prompt);
expect(view.feedbackPrompt()).toEqual(prompt);
});
});
\ No newline at end of file
/** /**
* Created by gward on 7/9/14. Container that handles addition / deletion of arbitrary items.
*/
An item is any object that has a `getFieldValues()` method,
/** which should return a JSON-serializable representation
Container to store arbitrary DOM elements for insertion, deletion and installation of self-delete buttons of the item.
Containers copy "template" elements to create new items.
For example, to create a container for an item called "test_item",
the DOM should look something like:
<div id="test_container" />
<div id="test_item_template">
<div class="test_item_remove_button">Remove</div>
<p>This is the default value for the item.</p>
</div>
<div id="test_item_add_button">Add</div>
You can then initialize the container:
>>> var container = $("#test_container").get(0);
>>> var template = $("#test_item_template").get(0);
>>> var addButton = $("#test_item_add_button").get(0);
>>>
>>> container = OpenAssessment.Container(
>>> ContainerItem, {
>>> containerElement: container,
>>> templateElement: template,
>>> addButtonElement: addButton,
>>> removeButtonClass: "test_item_remove_button"
>>> containerItemClass: "test_item"
>>> }
>>> );
The container is responsible for initializing the "add" and "remove" buttons,
including for pre-existing items in the container element.
Templates elements are never deleted, so they should be hidden using CSS styles.
Args: Args:
element (DOM element): The DOM element representing the container (usually an OL or a UL) containerItem (object): The container item object used to access
selectorDictionary (dict): Has keys which map element (str) to Container Item classes to which they are related the contents of items in the container.
Kwargs:
containerElement (DOM element): The element representing the container.
templateElement (DOM element): The element containing the template for creating new items.
addButtonElement (DOM element): The element of the button used to add new items to the container.
removeButtonClass (string): The CSS class of the button that removes an item from the container.
There may be one of these for each item in the container.
containerItemClass (string): The CSS class of items in the container.
New items will be assigned this class.
Returns:
OpenAssessment.Container
**/ **/
OpenAssessment.Container = function(element, selectorDictionary){ OpenAssessment.Container = function(containerItem, kwargs) {
this.element = element; this.containerItem = containerItem;
this.selectorDictionary = selectorDictionary; this.containerElement = kwargs.containerElement;
this.installDeleteButtons(element); this.templateElement = kwargs.templateElement;
this.addButtonElement = kwargs.addButtonElement;
this.removeButtonClass = kwargs.removeButtonClass;
this.containerItemClass = kwargs.containerItemClass;
// Install a click handler for the add button
$(this.addButtonElement).click($.proxy(this.add, this));
// Find items already in the container and install click
// handlers for the delete buttons.
var container = this;
$("." + this.removeButtonClass, this.containerElement).click(
function(eventData) { container.remove(eventData.target); }
);
// Initialize existing items, in case they need to install their
// own event handlers.
$("." + this.containerItemClass, this.containerElement).each(
function(index, element) { new container.containerItem(element); }
);
}; };
OpenAssessment.Container.prototype = { OpenAssessment.Container.prototype = {
/** /**
Adds a new item of the specified type to the DOM. Adds a new item to the container.
If the type is unrecognized,
Throws:
An error that alerts the user that an unknown type was attempted to be added to the container.
**/ **/
add: function(selectorString){ add: function() {
var type = this.selectorDictionary[selectorString]; // Copy the template into the container
if (type == undefined){ // Remove any CSS IDs (since now the element is not unique)
throw 'The string: ('+ selectorString + ') is not known by this container.'; // and add the item class so we can find it later.
} $(this.templateElement)
this.element.append(this.getHtmlTemplate(selectorString)); .clone()
}, .removeAttr('id')
.toggleClass('is--hidden', false)
/** .toggleClass(this.containerItemClass, true)
Removes a specified item from the DOM. This is distinct from (and not used by) delete buttons. .appendTo($(this.containerElement));
Args: // Install a click handler for the delete button
index(int): The index of the specified item to remove. // Since we just added the new element to the container,
// it should be the last one.
var container = this;
var containerItem = $("." + this.containerItemClass, this.containerElement).last()
containerItem.find('.' + this.removeButtonClass)
.click(function(eventData) { container.remove(eventData.target); } );
**/ // Initialize the item, allowing it to install event handlers.
remove: function(index){ new this.containerItem(containerItem.get(0));
var count = 0;
$(this.element).children().each(function(){
if (count == index){
$(this).remove();
return;
}
count++;
});
}, },
/** /**
Installs delete buttons on all sections of a certain element. Remove the item associated with an element.
Called both when we create a container, and when we add new elements. If the element is not itself an item, traverse up the
DOM tree until an item is found.
Args: Args:
liveElement (Jquery Element): an element that allows us to define the scope of delete button creation. element (DOM element): An element representing the container item
or an element within the container item.
**/ **/
installDeleteButtons: function(liveElement){ remove: function(element) {
$('.openassessment_delete_button', liveElement).each(function() { $(element).closest("." + this.containerItemClass).remove();
$(this).click(function () {
liveElement.closest('.openassessment_deletable').remove();
});
});
}, },
/** /**
Gets the values that each container defines for itself, in the order in which they are Retrieves the values that each container defines for itself, in the order in which they are
presented in the DOM. presented in the DOM.
Returns: Returns:
(list of ...): The list of the values that each container item associates with itself. array: The values representing each container item.
Throws:
An exception which notifies the user if there is an element in their container that is not
recognized as a part of the containers definition.
**/ **/
getItemValues: function () { getItemValues: function () {
var values = []; var values = [];
var container = this; var container = this;
$(this.element).children().each(function(){
//Attempts to find the type of a given element
var classes = $(this).attr('class').split(/\s+/);
var type = undefined;
for(var i = 0; i < classes.length; i++){
var c = classes[i];
if (container.selectorDictionary[c] != undefined){
type = container.selectorDictionary[c];
}
}
// If we couldn't resolve the type, we throw an exception. $("." + this.containerItemClass, this.containerElement).each(
if (type == undefined){ function(index, element) {
throw 'An item with classes (' + classes.join(' ') + var containerItem = new container.containerItem(element);
') was not found in a dict of known container items.'; var fieldValues = containerItem.getFieldValues();
values.push(fieldValues);
} }
);
var item = new type($(this));
var value = item.getFieldValues();
values.push(value);
});
return values; return values;
}, },
/** /**
Returns the specific HTML template that is defined for its container items. Retrieve the element representing an item in this container.
Args:
index (int): The index of the item, starting from 0.
Returns: Returns:
(str): The HTML template associated with the container item's type. DOM element if the item is found, otherwise null.
**/ **/
getHtmlTemplate: function(selectorString){ getItemElement: function(index) {
var selector = '.' + selectorString + '.openassessment_template'; var element = $("." + this.containerItemClass, this.containerElement).get(index);
var element = $(selector, this.element); return (element !== undefined) ? element : null;
if (element.length == 0){ },
throw "There is not an element which matches the selector (" + selector + ")";
}
// Because we are assuming each template will be found within a div which it is the sole occupant of,
// we can make this call safely to get the body and enclosing tab.
return element.parent().html();
}
}; };
...@@ -3,36 +3,39 @@ The RubricOption Class used to construct and maintain references to rubric optio ...@@ -3,36 +3,39 @@ The RubricOption Class used to construct and maintain references to rubric optio
container object. Constructs a new RubricOption element. container object. Constructs a new RubricOption element.
Args: Args:
parent (OpenAssessment.Container): The container that the option is a member of. element (OpenAssessment.Container): The container that the option is a member of.
Returns: Returns:
OpenAssessment.RubricOption OpenAssessment.RubricOption
**/ **/
OpenAssessment.RubricOption = function(element){ OpenAssessment.RubricOption = function(element) {
this.element = element; this.element = element;
}; };
OpenAssessment.RubricOption.prototype = { OpenAssessment.RubricOption.prototype = {
/** /**
Finds the values currently entered in the Option's fields, and returns them in a dictionary to the user. Finds the values currently entered in the Option's fields, and returns them.
Returns: Returns:
(dict) of the form: object literal of the form:
{ {
'name': 'Real Bad', 'name': 'Real Bad',
'points': 1, 'points': 1,
'explanation': 'Essay was primarily composed of emojis.' 'explanation': 'Essay was primarily composed of emojis.'
} }
**/ **/
getFieldValues: function (){ getFieldValues: function () {
var name = $('.openassessment_criterion_option_name', this.element).prop('value');
var points = $('.openassessment_criterion_option_points', this.element).prop('value');
var explanation = $('.openassessment_criterion_option_explanation', this.element).prop('value');
return { return {
'name': name, name: OpenAssessment.Fields.stringField(
'points': points, $('.openassessment_criterion_option_name', this.element)
'explanation': explanation ),
points: OpenAssessment.Fields.intField(
$('.openassessment_criterion_option_points', this.element)
),
explanation: OpenAssessment.Fields.stringField(
$('.openassessment_criterion_option_explanation', this.element)
)
}; };
} }
}; };
...@@ -47,12 +50,15 @@ Args: ...@@ -47,12 +50,15 @@ Args:
Returns: Returns:
OpenAssessment.RubricCriterion OpenAssessment.RubricCriterion
**/ **/
OpenAssessment.RubricCriterion = function(element){ OpenAssessment.RubricCriterion = function(element) {
this.element = element; this.element = element;
this.optionContainer = new OpenAssessment.Container( this.optionContainer = new OpenAssessment.Container(
$('.openassessment_criterion_option_list'), OpenAssessment.RubricOption, {
{ containerElement: $(".openassessment_criterion_option_list", this.element).get(0),
'openassessment_criterion_option': OpenAssessment.RubricOption templateElement: $("#openassessment_option_template").get(0),
addButtonElement: $(".openassessment_criterion_add_option", this.element).get(0),
removeButtonClass: "openassessment_rubric_remove_button",
containerItemClass: "openassessment_criterion_option",
} }
); );
}; };
...@@ -60,34 +66,36 @@ OpenAssessment.RubricCriterion = function(element){ ...@@ -60,34 +66,36 @@ OpenAssessment.RubricCriterion = function(element){
OpenAssessment.RubricCriterion.prototype = { OpenAssessment.RubricCriterion.prototype = {
/** /**
Finds the values currently entered in the Criterion's fields, and returns them in a dictionary to the user. Finds the values currently entered in the Criterion's fields, and returns them.
Returns: Returns:
(dict) of the form: object literal of the form:
{ {
'name': 'Emoji Content', 'name': 'Emoji Content',
'prompt': 'How expressive was the author with their words, and how much did they rely on emojis?', 'prompt': 'How expressive was the author with their words, and how much did they rely on emojis?',
'feedback': 'optional', 'feedback': 'optional',
'options': [ 'options': [
{ {
'name': 'Real Bad', 'name': 'Real Bad',
'points': 1, 'points': 1,
'explanation': 'Essay was primarily composed of emojis.' 'explanation': 'Essay was primarily composed of emojis.'
} }
... ...
] ]
} }
**/ **/
getFieldValues: function (){ getFieldValues: function () {
var name = $('.openassessment_criterion_name', this.element).prop('value');
var prompt = $('.openassessment_criterion_prompt', this.element).prop('value');
var feedback = $('.openassessment_criterion_feedback', this.element).prop('value');
var options = this.optionContainer.getItemValues();
return { return {
'name': name, name: OpenAssessment.Fields.stringField(
'prompt': prompt, $('.openassessment_criterion_name', this.element)
'options': options, ),
'feedback': feedback prompt: OpenAssessment.Fields.stringField(
$('.openassessment_criterion_prompt', this.element)
),
feedback: OpenAssessment.Fields.stringField(
$('.openassessment_criterion_feedback', this.element)
),
options: this.optionContainer.getItemValues()
}; };
} }
}; };
\ No newline at end of file
...@@ -9,12 +9,15 @@ ...@@ -9,12 +9,15 @@
Returns: Returns:
OpenAssessment.StudioView OpenAssessment.StudioView
**/ **/
OpenAssessment.StudioView = function(runtime, element, server) { OpenAssessment.StudioView = function(runtime, element, server) {
this.runtime = runtime; this.runtime = runtime;
this.server = server; this.server = server;
// Initialize the tabs
$(".openassessment_editor_content_and_tabs", this.element).tabs();
// Initialize the prompt tab view // Initialize the prompt tab view
this.promptView = new OpenAssessment.EditPromptView( this.promptView = new OpenAssessment.EditPromptView(
$("#oa_prompt_editor_wrapper", this.element).get(0) $("#oa_prompt_editor_wrapper", this.element).get(0)
...@@ -38,53 +41,14 @@ OpenAssessment.StudioView = function(runtime, element, server) { ...@@ -38,53 +41,14 @@ OpenAssessment.StudioView = function(runtime, element, server) {
] ]
); );
this.liveElement = $(element); // Initialize the rubric tab view
this.rubricView = new OpenAssessment.EditRubricView(
var liveElement = this.liveElement; $("#oa_rubric_editor_wrapper", this.element).get(0)
// Captures the HTML definition of the original criterion element. This will be the template
// used for all other criterion creations
var criterionHtml = $(".openassessment_criterion", liveElement).parent().html();
// Replaces all instances of the original ID (1) with the new fake ID in the string
// representation of the Criterion LI. This is our new template, with a C-C-C marker to replace.
this.criterionHtmlTemplate = criterionHtml.replace(new RegExp("1", "g"), "C-C-C");
// Captures the HTML definition of the original option element. Note that there are TWO template
// tags that need to be replaced "C-C-C" for the Criterion ID, and "O-O-O" for the option ID.
var optionHtml = $(".openassessment_criterion_option", liveElement).parent().html();
var criteriaReplaced = optionHtml.replace(new RegExp("criterion_1", "g"), "criterion_C-C-C");
this.optionHtmlTemplate = criteriaReplaced.replace(new RegExp("option_1", "g"), "option_O-O-O");
// Start us off with an empty setup, and uses the adding method to add a criteria (which in turn will
// add an option). This design choice was made to ensure consistent practices in adding and removing,
// the logic of which is all maintained in the function calls.
this.numberOfCriteria = 0;
this.numberOfOptions = [];
this.rubricCriteriaSelectors = [];
this.rubricFeedbackPrompt = $('#openassessment_rubric_feedback', liveElement);
$('#openassessment_criterion_list', liveElement).empty();
this.addNewCriterionToRubric();
var view = this;
// Installs the save and cancel buttons
$(".openassessment_save_button", this.element).click(
function (eventData) { view.save(); }
);
$(".openassessment_cancel_button", this.element).click(
function (eventData) { view.cancel(); }
); );
// Adds the tabbing functionality // Install the save and cancel buttons
$(".openassessment_editor_content_and_tabs", this.element).tabs(); $(".openassessment_save_button", this.element).click($.proxy(this.save, this));
$(".openassessment_cancel_button", this.element).click($.proxy(this.cancel, this));
$('#openassessment_rubric_add_criterion', this.element).click(
function (eventData) {
view.addNewCriterionToRubric(liveElement);
}
);
}; };
OpenAssessment.StudioView.prototype = { OpenAssessment.StudioView.prototype = {
...@@ -108,285 +72,54 @@ OpenAssessment.StudioView.prototype = { ...@@ -108,285 +72,54 @@ OpenAssessment.StudioView.prototype = {
} }
} }
).fail(function (errMsg) { ).fail(function (errMsg) {
view.showError(errMsg); view.showError(errMsg);
}); });
}, },
/** /**
Make the user confirm that he/she wants to update a problem Make the user confirm that he/she wants to update a problem
that has already been released. that has already been released.
Args: Args:
onConfirm (function): A function that accepts no arguments, onConfirm (function): A function that accepts no arguments,
executed if the user confirms the update. executed if the user confirms the update.
**/ **/
confirmPostReleaseUpdate: function (onConfirm) { confirmPostReleaseUpdate: function (onConfirm) {
var msg = gettext("This problem has already been released. Any changes will apply only to future assessments."); var msg = gettext("This problem has already been released. Any changes will apply only to future assessments.");
// TODO: classier confirm dialog // TODO: classier confirm dialog
if (confirm(msg)) { if (confirm(msg)) { onConfirm(); }
onConfirm();
}
},
/**
Adds a new criterion to the rubric.
*/
addNewCriterionToRubric: function (){
var view = this;
var liveElement = this.liveElement;
// Always appends the new criterion to the end of the list, and we force linear ordering.
var newCriterionID = this.numberOfCriteria + 1;
this.numberOfCriteria += 1;
this.numberOfOptions[newCriterionID] = 0;
// Fills in the template with the new criterion ID
var criterionHtml = this.criterionHtmlTemplate.replace(new RegExp('C-C-C', 'g'), '' + newCriterionID);
// Adds the new criterion to the DOM
$("#openassessment_criterion_list", liveElement).append(criterionHtml);
// Now that we have altered our LiveElement, we need to "reset" it so that it recognizes the new criterion.
liveElement = $("#openassessment_criterion_" + newCriterionID);
$(".openassessment_criterion_option_list", liveElement).empty();
// Adds our selector elements for easy future access.
view.rubricCriteriaSelectors[newCriterionID] = {
criterion: liveElement,
name: $('.openassessment_criterion_name', liveElement).first(),
prompt: $('.openassessment_criterion_prompt', liveElement).first(),
options: [],
feedback: 'disabled'
};
// Defaults to no feedback
$('input:radio[value=disabled]', liveElement).prop('checked', true);
view.addNewOptionToCriterion(liveElement, newCriterionID);
// Adds a listener that will collapse/expand the criterion on click.
$('#openassessment_display_criterion_' + newCriterionID, liveElement) .change( function () {
if (this.checked){
$('#openassessment_criterion_body_' + newCriterionID, liveElement).fadeIn();
} else {
$('#openassessment_criterion_body_' + newCriterionID, liveElement).fadeOut();
}
});
// Adds a listener which will delete the criterion on a click of the remove button
// The methodology for deletion is to shift all information from previous elements down into
$("#openassessment_criterion_" + newCriterionID + "_remove", liveElement) .click( function (eventData) {
view.removeCriterionFromRubric(newCriterionID);
});
// Adds a listener which will add another option to the Criterion's definition.
$("#openassessment_criterion_" + newCriterionID + "_add_option", liveElement).click( function (eventData) {
view.addNewOptionToCriterion(liveElement, newCriterionID);
});
// Adds a listener which removes criterion feedback
$(".openassessment_feedback_remove_button", liveElement). click( function(eventData){
$(".openassessment_criterion_feedback_direction", liveElement).fadeOut();
$(".openassessment_criterion_feedback_header_open", liveElement).fadeOut();
$(".openassessment_criterion_feedback_header_closed", liveElement).fadeIn();
$(".openassessment_feedback_remove_button", liveElement).fadeOut();
view.rubricCriteriaSelectors[newCriterionID].hasFeedback = false;
});
// Adds a listener which adds criterion feedback if not already displayed.
$(".openassessment_criterion_feedback_header_closed", liveElement).click( function (eventData){
$(".openassessment_criterion_feedback_direction", liveElement).fadeIn();
$(".openassessment_criterion_feedback_header_open", liveElement).fadeIn();
$(".openassessment_criterion_feedback_header_closed", liveElement).fadeOut();
$(".openassessment_feedback_remove_button", liveElement).fadeIn();
view.rubricCriteriaSelectors[newCriterionID].hasFeedback = true;
});
// Hides the criterion header used for adding
$(".openassessment_criterion_feedback_header_closed", liveElement).hide();
},
/**
Removes a specified criterion from the problem's rubric definition.
Changes are made in the DOM, in support/control structures.
*/
removeCriterionFromRubric: function(criterionToRemove){
var view = this;
var numCriteria = view.numberOfCriteria;
var selectors = view.rubricCriteriaSelectors;
// Shifts all data from "higher up" criteria down one in order to allow us to delete the last
// element without deleting information input by the user
for (var i = criterionToRemove; i < numCriteria; i++){
// Shifts all criterion field values
selectors[i].name.prop('value', selectors[i+1].name.prop('value'));
selectors[i].prompt.prop('value', selectors[i+1].prompt.prop('value'));
selectors[i].feedback = selectors[i+1].feedback;
$('input:radio[value="disabled"]', selectors[i].criterion).prop('checked', true);
// Ensures that we won't delete information during the shift by ensuring that the option lists are of the
//same length. Note it doesn't matter what we add or delete, simply that the lengths add up.
while (view.numberOfOptions[i] < view.numberOfOptions[i+1]){
view.addNewOptionToCriterion(selectors[i].criteria, i);
}
while (view.numberOfOptions[i] > view.numberOfOptions[i+1]){
view.removeOptionFromCriterion(selectors[i].criteria, i, 1);
}
// Transfers all data from each option to the next within a criterion.
var options1 = selectors[i].options;
var options2 = selectors[i+1].options;
var numOptions2 = view.numberOfOptions[i+1];
for (var j = 1; j < numOptions2; j++){
options1[j].points.prop('value', options2[j].points.prop('value'));
options1[j].name.prop('value', options2[j].name.prop('value'));
options1[j].explanation.prop('value', options2[j].explanation.prop('value'));
}
}
// Physically removes the rubric criteria from the DOM
view.rubricCriteriaSelectors[view.rubricCriteriaSelectors.length - 1].criterion.remove();
// Deletes the criteria from our three tracking statistics/structures
view.rubricCriteriaSelectors = view.rubricCriteriaSelectors.slice(0,numCriteria);
view.numberOfOptions = view.numberOfOptions.slice(0, numCriteria);
view.numberOfCriteria -= 1;
}, },
/** /**
Initializes a new option for a given criterion. This code block dictates the methodology for Save the updated problem definition to the server.
adding and removing options to a rubric. **/
Args:
liveElement (DOM element): An element containing the criterion interface in the DOM.
criterionID (string): The specific number of the criterion the option is being added to
*/
addNewOptionToCriterion: function (liveElement, criterionID){
var view = this;
// Finds the ID that will be associated with the option (it will be added to the end)
var newOptionID = this.numberOfOptions[criterionID] + 1;
this.numberOfOptions[criterionID] += 1;
// Replaces the template values with the true criterion and option ID's, and appends it on to the DOM
var optionHtml = this.optionHtmlTemplate;
optionHtml = optionHtml.replace(new RegExp("C-C-C", 'g'), "" + criterionID);
optionHtml = optionHtml.replace(new RegExp("O-O-O", 'g'), "" + newOptionID);
$("#openassessment_criterion_" + criterionID + "_options", liveElement).append(optionHtml);
// Resets the Live Element to be the updated (and newly created) option.
liveElement = $("#openassessment_criterion_" + criterionID + "_option_" + newOptionID);
// Constructs and assigns a dictionary of all of the selectors we store for each option.
view.rubricCriteriaSelectors[criterionID].options[newOptionID] = {
option: liveElement,
points: $("#openassessment_criterion_" + criterionID + "_option_" + newOptionID + "_points", liveElement),
name: $("#openassessment_criterion_" + criterionID + "_option_" + newOptionID + "_name", liveElement),
explanation: $("#openassessment_criterion_" + criterionID + "_option_" + newOptionID + "_explanation", liveElement)
};
// Sets the remove behavior. When deleted, an option will shift all of the data "behind" it on the list
// of options toward itself, and then deletes the last element in the list. This ensures that our options
// are always increasing by one, and that the data doesn't remain tethered to where it was entered.
$("#openassessment_criterion_" + criterionID + "_option_" + newOptionID + "_remove", liveElement).click(
function(eventData){
view.removeOptionFromCriterion(liveElement, criterionID, newOptionID);
}
);
},
/**
Removes a specified element from the DOM and from all tracking data. Note that no action is
taken against the specified element, rather, data is shifted down the chain (to construct the
illusion that the specified element was deleted), and then the last element is actually deleted.
Args:
liveElement (DOM element): An element containing the criterion interface in the DOM.
criterionID (string): The criterion ID that we are deleting from
optionToRemove (string): The option ID that we are "deleting"
*/
removeOptionFromCriterion: function(liveElement, criterionID, optionToRemove) {
var view = this;
var numberOfOptions = view.numberOfOptions[criterionID];
var optionSelectors = view.rubricCriteriaSelectors[criterionID].options;
// Shifts all data down, then deletes the last element, to create the appearance we deleted the given
// elements.
for (var i = optionToRemove; i < numberOfOptions; i++){
// Utilizes stored selectors to perform the swaps.
optionSelectors[i].points.prop('value', optionSelectors[i+1].points.prop('value'));
optionSelectors[i].name.prop('value', optionSelectors[i+1].name.prop('value'));
optionSelectors[i].explanation.prop('value', optionSelectors[i+1].explanation.prop('value'));
}
optionSelectors[optionSelectors.length - 1].option.remove();
view.rubricCriteriaSelectors[criterionID].options =
view.rubricCriteriaSelectors[criterionID].options.slice(0, (optionSelectors.length - 1));
view.numberOfOptions[criterionID] -= 1;
},
/**
Save the updated XML definition to the server.
**/
updateEditorContext: function () { updateEditorContext: function () {
// Notify the client-side runtime that we are starting // Notify the client-side runtime that we are starting
// to save so it can show the "Saving..." notification // to save so it can show the "Saving..." notification
this.runtime.notify('save', {state: 'start'}); this.runtime.notify('save', {state: 'start'});
// Grabs values from all of our fields, and stores them in a format which can be easily validated.
var rubricCriteria = [];
for (var i = 1; i <= this.numberOfCriteria; i++){
var selectorDict = this.rubricCriteriaSelectors[i];
var criterionValueDict = {
order_num: i - 1,
name: selectorDict.name.prop('value'),
prompt: selectorDict.prompt.prop('value'),
feedback: $("#openassessment_criterion_" + i + "_feedback").val()
};
var optionSelectorList = selectorDict.options;
var optionValueList = [];
for (var j = 1; j <= this.numberOfOptions[i]; j++){
var optionSelectors = optionSelectorList[j];
optionValueList = optionValueList.concat([{
order_num: j-1,
points: parseInt(optionSelectors.points.val(), 10),
name: optionSelectors.name.val(),
explanation: optionSelectors.explanation.val()
}]);
}
criterionValueDict.options = optionValueList;
rubricCriteria = rubricCriteria.concat([criterionValueDict]);
}
var view = this; var view = this;
this.server.updateEditorContext({ this.server.updateEditorContext({
title: view.settingsView.displayName(),
prompt: view.promptView.promptText(), prompt: view.promptView.promptText(),
feedbackPrompt: this.rubricFeedbackPrompt.val(), feedbackPrompt: view.rubricView.feedbackPrompt(),
criteria: view.rubricView.criteriaDefinition(),
title: view.settingsView.displayName(),
submissionStart: view.settingsView.submissionStart(), submissionStart: view.settingsView.submissionStart(),
submissionDue: view.settingsView.submissionDue(), submissionDue: view.settingsView.submissionDue(),
criteria: rubricCriteria,
assessments: view.settingsView.assessmentsDescription() assessments: view.settingsView.assessmentsDescription()
}).done( }).done(
function () { // Notify the client-side runtime that we finished saving
// Notify the client-side runtime that we finished saving // so it can hide the "Saving..." notification.
// so it can hide the "Saving..." notification. // Then reload the view.
// Then reload the view. function () { view.runtime.notify('save', {state: 'end'}); }
view.runtime.notify('save', {state: 'end'}); ).fail(
}).fail(function (msg) { function (msg) { view.showError(msg); }
view.showError(msg); );
});
}, },
/** /**
Cancel editing. Cancel editing.
**/ **/
cancel: function () { cancel: function () {
// Notify the client-side runtime so it will close the editing modal. // Notify the client-side runtime so it will close the editing modal.
this.runtime.notify('cancel', {}); this.runtime.notify('cancel', {});
......
...@@ -3,38 +3,76 @@ Interface for editing rubric definitions. ...@@ -3,38 +3,76 @@ Interface for editing rubric definitions.
**/ **/
OpenAssessment.EditRubricView = function(element) { OpenAssessment.EditRubricView = function(element) {
this.element = element; this.element = element;
this.criteriaContainer = new OpenAssessment.Container(
OpenAssessment.RubricCriterion, {
containerElement: $("#openassessment_criterion_list", this.element).get(0),
templateElement: $("#openassessment_criterion_template", this.element).get(0),
addButtonElement: $("#openassessment_rubric_add_criterion", this.element).get(0),
removeButtonClass: "openassessment_rubric_remove_button",
containerItemClass: "openassessment_criterion",
}
);
}; };
OpenAssessment.EditRubricView.prototype = { OpenAssessment.EditRubricView.prototype = {
/** /**
Install event handlers. Construct a list of criteria definitions from the editor UI.
**/
load: function() {
//this.container = new Container(this.element, "openassessment__rubric__criterion", OpenAssessment.RubricCriterion);
},
/** Returns:
list of criteria objects
Example usage:
>>> editRubricView.criteriaDefinition();
[ [
{ {
name: "Criterion",
prompt: "Prompt",
order_num: 0, order_num: 0,
name: 'Criteria!' feedback: "disabled",
prompt: 'prompt',
feedback: 'disabled',
options: [ options: [
{ {
order_num: 0, order_num: 0,
name: 'name', points: 1,
explanation: 'explanation', name: "Good",
points: 1 explanation: "Explanation"
}, },
... ...
] ]
}, },
... ...
] ]
**/ **/
criteriaDefinition: function() { criteriaDefinition: function() {
//return this.container.getItemValues(); var criteria = this.criteriaContainer.getItemValues();
// Add order_num fields for criteria and options
for (var criterion_idx = 0; criterion_idx < criteria.length; criterion_idx++) {
var criterion = criteria[criterion_idx];
criterion.order_num = criterion_idx;
for (var option_idx = 0; option_idx < criterion.options.length; option_idx++) {
var option = criterion.options[option_idx];
option.order_num = option_idx;
}
}
return criteria;
}, },
/**
Get or set the feedback prompt in the editor.
This is the prompt shown to students when giving "overall" feedback
on a submission.
Args:
text (string, optional): If provided, set the feedback prompt to this value.
Returns:
string
**/
feedbackPrompt: function(text) {
var sel = $("#openassessment_rubric_feedback", this.element);
return OpenAssessment.Fields.stringField(sel, text);
}
}; };
\ No newline at end of file
...@@ -20,6 +20,9 @@ logger = logging.getLogger(__name__) ...@@ -20,6 +20,9 @@ logger = logging.getLogger(__name__)
class StudioMixin(object): class StudioMixin(object):
"""
Studio editing view for OpenAssessment XBlock.
"""
DEFAULT_CRITERIA = [ DEFAULT_CRITERIA = [
{ {
...@@ -30,10 +33,6 @@ class StudioMixin(object): ...@@ -30,10 +33,6 @@ class StudioMixin(object):
} }
] ]
"""
Studio editing view for OpenAssessment XBlock.
"""
def studio_view(self, context=None): def studio_view(self, context=None):
""" """
Render the OpenAssessment XBlock for editing in Studio. Render the OpenAssessment XBlock for editing in Studio.
......
#!/usr/bin/env bash #!/usr/bin/env bash
cd `dirname $BASH_SOURCE` && cd ../openassessment/xblock/static cd `dirname $BASH_SOURCE` && cd ../openassessment/xblock/static
sass --update sass:css --force sass --update sass:css --force --style compressed
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