Commit aa8d2226 by cahrens

New example jsinput problem.

TNL-5893
parent c3c86b8b
---
metadata:
display_name: Custom Javascript Display and Grading
display_name: Custom JavaScript Display and Grading
markdown: !!null
showanswer: never
data: |
......@@ -8,8 +8,8 @@ data: |
<p>
In these problems (also called custom JavaScript problems or JS Input
problems), you add a problem or tool that uses JavaScript in Studio.
Studio embeds the problem in an IFrame so that your students can
interact with it in the LMS. You can grade your students' work using
Studio embeds the problem in an IFrame so that your learners can
interact with it in the LMS. You can grade your learners' work using
JavaScript and some basic Python, and the grading is integrated into the
edX grading system.
</p>
......@@ -31,42 +31,47 @@ data: |
<p>
When you add the problem, be sure to select <strong>Settings</strong>
to specify a <strong>Display Name</strong> and other values that apply.
Also, be sure to specify a <strong>title</strong> attribute on the <strong>jsinput</strong> tag;
this title is used for the title attribute on the generated IFrame. Generally,
the title attribute on the IFrame should match the title tag of the HTML hosted
within the IFrame, which is specified by the <strong>html_file</strong> attribute.
</p>
<p>You can use the following example problem as a model.</p>
<customresponse cfn="vglcfn">
<customresponse cfn="check_function">
<script type="loncapa/python">
<![CDATA[
import json
def vglcfn(e, ans):
'''
par is a dictionary that contains two keys, "answer" and "state".
def check_function(e, ans):
"""
"response" is a dictionary that contains two keys, "answer" and "state".
The value of "answer" is the JSON string that "getGrade" returns.
The value of "state" is the JSON string that "getState" returns.
Clicking either "Submit" or "Save" registers the current state.
"""
response = json.loads(ans)
'''
par = json.loads(ans)
# You can use the value of the answer key to grade:
answer = json.loads(par["answer"])
return answer["cylinder"] and not answer["cube"]
'''
answer = json.loads(response["answer"])
return answer == "correct"
# Or you can use the value of the state key to grade:
state = json.loads(par["state"])
selectedObjects = state["selectedObjects"]
return selectedObjects["cylinder"] and not selectedObjects["cube"]
'''
"""
state = json.loads(response["state"])
return state["selectedChoice"] == "correct"
"""
]]>
</script>
<p>In the following image, click the objects until the cone is yellow and the cube is blue.</p>
<jsinput gradefn="WebGLDemo.getGrade"
get_statefn="WebGLDemo.getState"
set_statefn="WebGLDemo.setState"
initial_state='{"selectedObjects":{"cube":true,"cylinder":false}}'
width="400"
height="400"
html_file="https://studio.edx.org/c4x/edX/DemoX/asset/webGLDemo.html"
title="Spinning Cone and Cube"
<p>This is paragraph text displayed before the IFrame.</p>
<jsinput
gradefn="JSInputDemo.getGrade"
get_statefn="JSInputDemo.getState"
set_statefn="JSInputDemo.setState"
initial_state='{"selectedChoice": "incorrect1", "availableChoices": ["incorrect1", "correct", "incorrect2"]}'
width="600"
height="100"
html_file="https://files.edx.org/custom-js-example/jsinput_example.html"
title="Dropdown with Dynamic Text"
sop="false"/>
</customresponse>
</problem>
.directions {
font-size: large
}
.feedback {
font-size: medium;
border: 2px solid cornflowerblue;
padding: 5px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Dropdown with Dynamic Text</title>
<link rel="stylesheet" type="text/css" href="https://files.edx.org/custom-js-example/jsinput_example.css">
</head>
<body>
<script src="https://files.edx.org/custom-js-example/jschannel.js"></script>
<script src="https://files.edx.org/custom-js-example/jsinput_example.js" defer></script>
<label class="directions">Select an option from the list:
<select class="choices"></select>
</label>
<p aria-live="polite" class="feedback"></p>
</body>
</html>
/* globals Channel */
(function() {
'use strict';
// state will be populated via initial_state via the `setState` method. Defining dummy values here
// to make the expected structure clear.
var state = {
availableChoices: [],
selectedChoice: ''
},
channel,
select = document.getElementsByClassName('choices')[0],
feedback = document.getElementsByClassName('feedback')[0];
function populateSelect() {
// Populate the select from `state.availableChoices`.
var i, option;
// Clear out any pre-existing options.
while (select.firstChild) {
select.removeChild(select.firstChild);
}
// Populate the select with the available choices.
for (i = 0; i < state.availableChoices.length; i++) {
option = document.createElement('option');
option.value = i;
option.innerHTML = state.availableChoices[i];
if (state.availableChoices[i] === state.selectedChoice) {
option.selected = true;
}
select.appendChild(option);
}
feedback.innerText = "The currently selected answer is '" + state.selectedChoice + "'.";
}
function getGrade() {
// The following return value may or may not be used to grade server-side.
// If getState and setState are used, then the Python grader also gets access
// to the return value of getState and can choose it instead to grade.
return JSON.stringify(state.selectedChoice);
}
function getState() {
// Returns the current state (which can be used for grading).
return JSON.stringify(state);
}
// This function will be called with 1 argument when JSChannel is not used,
// 2 otherwise. In the latter case, the first argument is a transaction
// object that will not be used here
// (see http://mozilla.github.io/jschannel/docs/)
function setState() {
var stateString = arguments.length === 1 ? arguments[0] : arguments[1];
state = JSON.parse(stateString);
populateSelect();
}
// Establish a channel only if this application is embedded in an iframe.
// This will let the parent window communicate with this application using
// RPC and bypass SOP restrictions.
if (window.parent !== window) {
channel = Channel.build({
window: window.parent,
origin: '*',
scope: 'JSInput'
});
channel.bind('getGrade', getGrade);
channel.bind('getState', getState);
channel.bind('setState', setState);
}
select.addEventListener('change', function() {
state.selectedChoice = select.options[select.selectedIndex].text;
feedback.innerText = "You have selected '" + state.selectedChoice +
"'. Click Submit to grade your answer.";
});
return {
getState: getState,
setState: setState,
getGrade: getGrade
};
}());
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