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.
@@ -31,42 +31,47 @@ data: |
When you add the problem, be sure to select Settings
to specify a Display Name and other values that apply.
+ Also, be sure to specify a title attribute on the jsinput 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 html_file attribute.
You can use the following example problem as a model.
-
+
-
In the following image, click the objects until the cone is yellow and the cube is blue.
- This is paragraph text displayed before the IFrame.
+
diff --git a/common/static/css/capa/jsinput_css.css b/common/static/css/capa/jsinput_css.css
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/common/static/js/capa/jsinput/jsinput_example.css b/common/static/js/capa/jsinput/jsinput_example.css
new file mode 100644
index 0000000000..8f1144ac40
--- /dev/null
+++ b/common/static/js/capa/jsinput/jsinput_example.css
@@ -0,0 +1,9 @@
+.directions {
+ font-size: large
+}
+
+.feedback {
+ font-size: medium;
+ border: 2px solid cornflowerblue;
+ padding: 5px;
+}
diff --git a/common/static/js/capa/jsinput/jsinput_example.html b/common/static/js/capa/jsinput/jsinput_example.html
new file mode 100644
index 0000000000..caa034fff9
--- /dev/null
+++ b/common/static/js/capa/jsinput/jsinput_example.html
@@ -0,0 +1,15 @@
+
+
+
+ Dropdown with Dynamic Text
+
+
+
+
+
+
+
+
+
diff --git a/common/static/js/capa/jsinput/jsinput_example.js b/common/static/js/capa/jsinput/jsinput_example.js
new file mode 100644
index 0000000000..6aa125fd69
--- /dev/null
+++ b/common/static/js/capa/jsinput/jsinput_example.js
@@ -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
+ };
+}());
diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt
index 238bf9e699..84c7cc2e1d 100644
--- a/requirements/edx/github.txt
+++ b/requirements/edx/github.txt
@@ -53,7 +53,7 @@ git+https://github.com/edx/MongoDBProxy.git@25b99097615bda06bd7cdfe5669ed80dc2a7
git+https://github.com/edx/nltk.git@2.0.6#egg=nltk==2.0.6
-e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e git+https://github.com/appliedsec/pygeoip.git@95e69341cebf5a6a9fbf7c4f5439d458898bdc3b#egg=pygeoip
--e git+https://github.com/jazkarta/edx-jsme.git@0908b4db16168382be5685e7e9b7b4747ac410e0#egg=edx-jsme
+-e git+https://github.com/jazkarta/edx-jsme.git@690dbf75441fa91c7c4899df0b83d77f7deb5458#egg=edx-jsme
git+https://github.com/edx/django-pyfs.git@1.0.3#egg=django-pyfs==1.0.3
git+https://github.com/mitodl/django-cas.git@v2.1.1#egg=django-cas
-e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest