New example jsinput problem.
TNL-5893
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
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>
|
||||
|
||||
9
common/static/js/capa/jsinput/jsinput_example.css
Normal file
9
common/static/js/capa/jsinput/jsinput_example.css
Normal file
@@ -0,0 +1,9 @@
|
||||
.directions {
|
||||
font-size: large
|
||||
}
|
||||
|
||||
.feedback {
|
||||
font-size: medium;
|
||||
border: 2px solid cornflowerblue;
|
||||
padding: 5px;
|
||||
}
|
||||
15
common/static/js/capa/jsinput/jsinput_example.html
Normal file
15
common/static/js/capa/jsinput/jsinput_example.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!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>
|
||||
86
common/static/js/capa/jsinput/jsinput_example.js
Normal file
86
common/static/js/capa/jsinput/jsinput_example.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 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
|
||||
};
|
||||
}());
|
||||
Reference in New Issue
Block a user