Commit a1d3c316 by cahrens

eslint fixes and other cleanup

parent 1b03a009
...@@ -45,7 +45,6 @@ common/lib/xmodule/xmodule/js/spec/problem/edit_spec_hint.js ...@@ -45,7 +45,6 @@ common/lib/xmodule/xmodule/js/spec/problem/edit_spec_hint.js
common/lib/xmodule/xmodule/js/spec/tabs/edit.js common/lib/xmodule/xmodule/js/spec/tabs/edit.js
common/lib/xmodule/xmodule/js/src/annotatable/display.js common/lib/xmodule/xmodule/js/src/annotatable/display.js
common/lib/xmodule/xmodule/js/src/capa/display.js
common/lib/xmodule/xmodule/js/src/conditional/display.js common/lib/xmodule/xmodule/js/src/conditional/display.js
common/lib/xmodule/xmodule/js/src/discussion/display.js common/lib/xmodule/xmodule/js/src/discussion/display.js
common/lib/xmodule/xmodule/js/src/html/display.js common/lib/xmodule/xmodule/js/src/html/display.js
......
// Generated by CoffeeScript 1.6.1 /* global MathJax, Collapsible, interpolate, JavascriptLoader, Logger, CodeMirror */
(function () { // Note: this code was originally converted from CoffeeScript, and thus follows some
var _this = this, // coding conventions that are discouraged by eslint. Some warnings have been suppressed
__indexOf = [].indexOf || function (item) { // to avoid substantial rewriting of the code. Allow the eslint suppressions to exceed
for (var i = 0, l = this.length; i < l; i++) { // the max line length of 120.
if (i in this && this[i] === item) return i; /* eslint max-len: ["error", 120, { "ignoreComments": true }] */
}
return -1;
};
this.Problem = (function () { (function() {
var _this = this; 'use strict';
var indexOfHelper = [].indexOf ||
function(item) {
var i, len;
for (i = 0, len = this.length; i < len; i++) {
if (i in this && this[i] === item) {
return i;
}
}
return -1;
};
this.Problem = (function() {
function Problem(element) { function Problem(element) {
var _this = this; var that = this;
this.hint_button = function () { this.hint_button = function() {
return Problem.prototype.hint_button.apply(_this, arguments); return Problem.prototype.hint_button.apply(that, arguments);
}; };
this.enableSubmitButtonAfterTimeout = function () { this.enableSubmitButtonAfterTimeout = function() {
return Problem.prototype.enableSubmitButtonAfterTimeout.apply(_this, arguments); return Problem.prototype.enableSubmitButtonAfterTimeout.apply(that, arguments);
}; };
this.enableSubmitButtonAfterResponse = function () { this.enableSubmitButtonAfterResponse = function() {
return Problem.prototype.enableSubmitButtonAfterResponse.apply(_this, arguments); return Problem.prototype.enableSubmitButtonAfterResponse.apply(that, arguments);
}; };
this.enableSubmitButton = function (enable, changeText) { this.enableSubmitButton = function(enable, changeText) {
if (changeText == null) { if (changeText === null || changeText === undefined) {
changeText = true; changeText = true; // eslint-disable-line no-param-reassign
} }
return Problem.prototype.enableSubmitButton.apply(_this, arguments); return Problem.prototype.enableSubmitButton.apply(that, arguments);
}; };
this.enableAllButtons = function (enable, isFromCheckOperation) { this.enableAllButtons = function(enable, isFromCheckOperation) { // eslint-disable-line no-unused-vars
return Problem.prototype.enableAllButtons.apply(_this, arguments); return Problem.prototype.enableAllButtons.apply(that, arguments);
}; };
this.disableAllButtonsWhileRunning = function (operationCallback, isFromCheckOperation) { this.disableAllButtonsWhileRunning = function(
return Problem.prototype.disableAllButtonsWhileRunning.apply(_this, arguments); operationCallback, isFromCheckOperation // eslint-disable-line no-unused-vars
) {
return Problem.prototype.disableAllButtonsWhileRunning.apply(that, arguments);
}; };
this.submitAnswersAndSubmitButton = function (bind) { this.submitAnswersAndSubmitButton = function(bind) {
if (bind == null) { if (bind === null || bind === undefined) {
bind = false; bind = false; // eslint-disable-line no-param-reassign
} }
return Problem.prototype.submitAnswersAndSubmitButton.apply(_this, arguments); return Problem.prototype.submitAnswersAndSubmitButton.apply(that, arguments);
}; };
this.refreshAnswers = function () { this.refreshAnswers = function() {
return Problem.prototype.refreshAnswers.apply(_this, arguments); return Problem.prototype.refreshAnswers.apply(that, arguments);
}; };
this.updateMathML = function (jax, element) { this.updateMathML = function(jax, el) { // eslint-disable-line no-unused-vars
return Problem.prototype.updateMathML.apply(_this, arguments); return Problem.prototype.updateMathML.apply(that, arguments);
}; };
this.refreshMath = function (event, element) { this.refreshMath = function(event, el) { // eslint-disable-line no-unused-vars
return Problem.prototype.refreshMath.apply(_this, arguments); return Problem.prototype.refreshMath.apply(that, arguments);
}; };
this.save_internal = function () { this.save_internal = function() {
return Problem.prototype.save_internal.apply(_this, arguments); return Problem.prototype.save_internal.apply(that, arguments);
}; };
this.save = function () { this.save = function() {
return Problem.prototype.save.apply(_this, arguments); return Problem.prototype.save.apply(that, arguments);
}; };
this.gentle_alert = function (msg) { this.gentle_alert = function(msg) { // eslint-disable-line no-unused-vars
return Problem.prototype.gentle_alert.apply(_this, arguments); return Problem.prototype.gentle_alert.apply(that, arguments);
}; };
this.clear_all_notifications = function () { this.clear_all_notifications = function() {
return Problem.prototype.clear_all_notifications.apply(_this, arguments); return Problem.prototype.clear_all_notifications.apply(that, arguments);
}; };
this.show = function () { this.show = function() {
return Problem.prototype.show.apply(_this, arguments); return Problem.prototype.show.apply(that, arguments);
}; };
this.reset_internal = function () { this.reset_internal = function() {
return Problem.prototype.reset_internal.apply(_this, arguments); return Problem.prototype.reset_internal.apply(that, arguments);
}; };
this.reset = function () { this.reset = function() {
return Problem.prototype.reset.apply(_this, arguments); return Problem.prototype.reset.apply(that, arguments);
}; };
this.get_sr_status = function (contents) { this.get_sr_status = function(contents) { // eslint-disable-line no-unused-vars
return Problem.prototype.get_sr_status.apply(_this, arguments); return Problem.prototype.get_sr_status.apply(that, arguments);
}; };
this.submit_internal = function () { this.submit_internal = function() {
return Problem.prototype.submit_internal.apply(_this, arguments); return Problem.prototype.submit_internal.apply(that, arguments);
}; };
this.submit = function () { this.submit = function() {
return Problem.prototype.submit.apply(_this, arguments); return Problem.prototype.submit.apply(that, arguments);
}; };
this.submit_fd = function () { this.submit_fd = function() {
return Problem.prototype.submit_fd.apply(_this, arguments); return Problem.prototype.submit_fd.apply(that, arguments);
}; };
this.focus_on_save_notification = function () { this.focus_on_save_notification = function() {
return Problem.prototype.focus_on_save_notification.apply(_this, arguments); return Problem.prototype.focus_on_save_notification.apply(that, arguments);
}; };
this.focus_on_hint_notification = function () { this.focus_on_hint_notification = function() {
return Problem.prototype.focus_on_hint_notification.apply(_this, arguments); return Problem.prototype.focus_on_hint_notification.apply(that, arguments);
}; };
this.focus_on_submit_notification = function () { this.focus_on_submit_notification = function() {
return Problem.prototype.focus_on_submit_notification.apply(_this, arguments); return Problem.prototype.focus_on_submit_notification.apply(that, arguments);
}; };
this.focus_on_notification = function (type) { this.focus_on_notification = function(type) { // eslint-disable-line no-unused-vars
return Problem.prototype.focus_on_notification.apply(_this, arguments); return Problem.prototype.focus_on_notification.apply(that, arguments);
}; };
this.scroll_to_problem_meta = function () { this.scroll_to_problem_meta = function() {
return Problem.prototype.scroll_to_problem_meta.apply(_this, arguments); return Problem.prototype.scroll_to_problem_meta.apply(that, arguments);
}; };
this.submit_save_waitfor = function (callback) { this.submit_save_waitfor = function(callback) { // eslint-disable-line no-unused-vars
return Problem.prototype.submit_save_waitfor.apply(_this, arguments); return Problem.prototype.submit_save_waitfor.apply(that, arguments);
}; };
this.setupInputTypes = function () { this.setupInputTypes = function() {
return Problem.prototype.setupInputTypes.apply(_this, arguments); return Problem.prototype.setupInputTypes.apply(that, arguments);
}; };
this.poll = function (prev_timeout, focus_callback) { this.poll = function(prevTimeout, focusCallback // eslint-disable-line no-unused-vars
return Problem.prototype.poll.apply(_this, arguments); ) {
return Problem.prototype.poll.apply(that, arguments);
}; };
this.queueing = function (focus_callback) { this.queueing = function(focusCallback) { // eslint-disable-line no-unused-vars
return Problem.prototype.queueing.apply(_this, arguments); return Problem.prototype.queueing.apply(that, arguments);
}; };
this.forceUpdate = function (response) { this.forceUpdate = function(response) { // eslint-disable-line no-unused-vars
return Problem.prototype.forceUpdate.apply(_this, arguments); return Problem.prototype.forceUpdate.apply(that, arguments);
}; };
this.updateProgress = function (response) { this.updateProgress = function(response) { // eslint-disable-line no-unused-vars
return Problem.prototype.updateProgress.apply(_this, arguments); return Problem.prototype.updateProgress.apply(that, arguments);
}; };
this.renderProgressState = function () { this.renderProgressState = function() {
return Problem.prototype.renderProgressState.apply(_this, arguments); return Problem.prototype.renderProgressState.apply(that, arguments);
}; };
this.bind = function () { this.bind = function() {
return Problem.prototype.bind.apply(_this, arguments); return Problem.prototype.bind.apply(that, arguments);
}; };
this.el = $(element).find('.problems-wrapper'); this.el = $(element).find('.problems-wrapper');
this.id = this.el.data('problem-id'); this.id = this.el.data('problem-id');
this.element_id = this.el.attr('id'); this.element_id = this.el.attr('id');
this.url = this.el.data('url'); this.url = this.el.data('url');
this.content = this.el.data('content'); this.content = this.el.data('content');
// has_timed_out and has_response are used to ensure that
// we wait a minimum of ~ 1s before transitioning the submit
// button from disabled to enabled
this.has_timed_out = false; this.has_timed_out = false;
this.has_response = false; this.has_response = false;
this.render(this.content); this.render(this.content);
} }
Problem.prototype.$ = function (selector) { Problem.prototype.$ = function(selector) {
return $(selector, this.el); return $(selector, this.el);
}; };
Problem.prototype.bind = function () { Problem.prototype.bind = function() {
var problem_prefix, var problemPrefix,
_this = this; that = this;
if (typeof MathJax !== "undefined" && MathJax !== null) { if (typeof MathJax !== 'undefined' && MathJax !== null) {
this.el.find('.problem > div').each(function (index, element) { this.el.find('.problem > div').each(function(index, element) {
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, element]); return MathJax.Hub.Queue(['Typeset', MathJax.Hub, element]);
}); });
} }
window.update_schematics(); window.update_schematics();
problem_prefix = this.element_id.replace(/problem_/, ''); problemPrefix = this.element_id.replace(/problem_/, '');
this.inputs = this.$("[id^='input_" + problem_prefix + "_']"); this.inputs = this.$('[id^="input_' + problemPrefix + '_"]');
this.$('div.action button').click(this.refreshAnswers); this.$('div.action button').click(this.refreshAnswers);
this.reviewButton = this.$('.notification-btn.review-btn'); this.reviewButton = this.$('.notification-btn.review-btn');
this.reviewButton.click(this.scroll_to_problem_meta); this.reviewButton.click(this.scroll_to_problem_meta);
...@@ -162,22 +177,23 @@ ...@@ -162,22 +177,23 @@
this.showButton.click(this.show); this.showButton.click(this.show);
this.saveButton = this.$('.action .save'); this.saveButton = this.$('.action .save');
this.saveNotification = this.$('.notification-save'); this.saveNotification = this.$('.notification-save');
this.saveButtonLabel = this.$('.action .save .save-label');
this.saveButton.click(this.save); this.saveButton.click(this.save);
this.gentleAlertNotification = this.$('.notification-gentle-alert'); this.gentleAlertNotification = this.$('.notification-gentle-alert');
this.submitNotification = this.$('.notification-submit'); this.submitNotification = this.$('.notification-submit');
this.$('.clarification').focus(function (ev) {
// Accessibility helper for sighted keyboard users to show <clarification> tooltips on focus:
this.$('.clarification').focus(function(ev) {
var icon; var icon;
icon = $(ev.target).children("i"); icon = $(ev.target).children('i');
return window.globalTooltipManager.openTooltip(icon); return window.globalTooltipManager.openTooltip(icon);
}); });
this.$('.clarification').blur(function (ev) { this.$('.clarification').blur(function() {
return window.globalTooltipManager.hide(); return window.globalTooltipManager.hide();
}); });
this.$('.review-btn').focus(function (ev) { this.$('.review-btn').focus(function(ev) {
return $(ev.target).removeClass('sr'); return $(ev.target).removeClass('sr');
}); });
this.$('.review-btn').blur(function (ev) { this.$('.review-btn').blur(function(ev) {
return $(ev.target).addClass('sr'); return $(ev.target).addClass('sr');
}); });
this.bindResetCorrectness(); this.bindResetCorrectness();
...@@ -186,56 +202,80 @@ ...@@ -186,56 +202,80 @@
} }
Collapsible.setCollapsibles(this.el); Collapsible.setCollapsibles(this.el);
this.$('input.math').keyup(this.refreshMath); this.$('input.math').keyup(this.refreshMath);
if (typeof MathJax !== "undefined" && MathJax !== null) { if (typeof MathJax !== 'undefined' && MathJax !== null) {
return this.$('input.math').each(function (index, element) { this.$('input.math').each(function(index, element) {
return MathJax.Hub.Queue([_this.refreshMath, null, element]); return MathJax.Hub.Queue([that.refreshMath, null, element]);
}); });
} }
}; };
Problem.prototype.renderProgressState = function () { Problem.prototype.renderProgressState = function() {
var a, detail, earned, graded, possible, progress, progress_template, status; var a, detail, earned, graded, possible, progress, progressTemplate, status;
detail = this.el.data('progress_detail'); detail = this.el.data('progress_detail');
status = this.el.data('progress_status'); status = this.el.data('progress_status');
graded = this.el.data('graded'); graded = this.el.data('graded');
if (status !== 'none' && (detail != null) && (jQuery.type(detail) === "string") && detail.indexOf('/') > 0) {
// Render 'x/y point(s)' if student has attempted question
if (status !== 'none' && (detail !== null && detail !== undefined) && (jQuery.type(detail) === 'string') &&
detail.indexOf('/') > 0) {
a = detail.split('/'); a = detail.split('/');
earned = parseFloat(a[0]); earned = parseFloat(a[0]);
possible = parseFloat(a[1]); possible = parseFloat(a[1]);
if (graded === "True" && possible !== 0) { if (graded === 'True' && possible !== 0) {
// Translators: %(earned)s is the number of points earned. %(possible)s is the total number of points (examples: 0/1, 1/1, 2/3, 5/10). The total number of points will always be at least 1. We pluralize based on the total number of points (example: 0/1 point; 1/2 points); progressTemplate = ngettext(
progress_template = ngettext('%(earned)s/%(possible)s point (graded)', '%(earned)s/%(possible)s points (graded)', possible); // This comment needs to be on one line to be properly scraped for the translators.
// Translators: %(earned)s is the number of points earned. %(possible)s is the total number of points (examples: 0/1, 1/1, 2/3, 5/10). The total number of points will always be at least 1. We pluralize based on the total number of points (example: 0/1 point; 1/2 points);
'%(earned)s/%(possible)s point (graded)', '%(earned)s/%(possible)s points (graded)',
possible
);
} else { } else {
// Translators: %(earned)s is the number of points earned. %(possible)s is the total number of points (examples: 0/1, 1/1, 2/3, 5/10). The total number of points will always be at least 1. We pluralize based on the total number of points (example: 0/1 point; 1/2 points); progressTemplate = ngettext(
progress_template = ngettext('%(earned)s/%(possible)s point (ungraded)', '%(earned)s/%(possible)s points (ungraded)', possible); // This comment needs to be on one line to be properly scraped for the translators.
// Translators: %(earned)s is the number of points earned. %(possible)s is the total number of points (examples: 0/1, 1/1, 2/3, 5/10). The total number of points will always be at least 1. We pluralize based on the total number of points (example: 0/1 point; 1/2 points);
'%(earned)s/%(possible)s point (ungraded)', '%(earned)s/%(possible)s points (ungraded)',
possible
);
} }
progress = interpolate(progress_template, { progress = interpolate(
'earned': earned, progressTemplate, {
'possible': possible earned: earned,
}, true); possible: possible
}, true
);
} }
// Render 'x point(s) possible' if student has not yet attempted question
// Status is set to none when a user has a score of 0, and 0 when the problem has a weight of 0.
if (status === 'none' || status === 0) { if (status === 'none' || status === 0) {
if ((detail != null) && (jQuery.type(detail) === "string") && detail.indexOf('/') > 0) { if ((detail !== null && detail !== undefined) && (jQuery.type(detail) === 'string') &&
detail.indexOf('/') > 0) {
a = detail.split('/'); a = detail.split('/');
possible = parseFloat(a[1]); possible = parseFloat(a[1]);
} else { } else {
possible = 0; possible = 0;
} }
if (graded === "True" && possible !== 0) { if (graded === 'True' && possible !== 0) {
// Translators: %(num_points)s is the number of points possible (examples: 1, 3, 10).; progressTemplate = ngettext(
progress_template = ngettext("%(num_points)s point possible (graded)", "%(num_points)s points possible (graded)", possible); // Translators: %(num_points)s is the number of points possible (examples: 1, 3, 10).;
'%(num_points)s point possible (graded)', '%(num_points)s points possible (graded)',
possible
);
} else { } else {
// Translators: %(num_points)s is the number of points possible (examples: 1, 3, 10).; progressTemplate = ngettext(
progress_template = ngettext("%(num_points)s point possible (ungraded)", "%(num_points)s points possible (ungraded)", possible); // Translators: %(num_points)s is the number of points possible (examples: 1, 3, 10).;
'%(num_points)s point possible (ungraded)', '%(num_points)s points possible (ungraded)',
possible
);
} }
progress = interpolate(progress_template, { progress = interpolate(
'num_points': possible progressTemplate,
}, true); {num_points: possible}, true
);
} }
return this.$('.problem-progress').text(progress); return this.$('.problem-progress').text(progress);
}; };
Problem.prototype.updateProgress = function (response) { Problem.prototype.updateProgress = function(response) {
if (response.progress_changed) { if (response.progress_changed) {
this.el.data('progress_status', response.progress_status); this.el.data('progress_status', response.progress_status);
this.el.data('progress_detail', response.progress_detail); this.el.data('progress_detail', response.progress_detail);
...@@ -244,128 +284,161 @@ ...@@ -244,128 +284,161 @@
return this.renderProgressState(); return this.renderProgressState();
}; };
Problem.prototype.forceUpdate = function (response) { Problem.prototype.forceUpdate = function(response) {
this.el.data('progress_status', response.progress_status); this.el.data('progress_status', response.progress_status);
this.el.data('progress_detail', response.progress_detail); this.el.data('progress_detail', response.progress_detail);
this.el.trigger('progressChanged'); this.el.trigger('progressChanged');
return this.renderProgressState(); return this.renderProgressState();
}; };
Problem.prototype.queueing = function (focus_callback) { Problem.prototype.queueing = function(focusCallback) {
var _this = this; var that = this;
this.queued_items = this.$(".xqueue"); this.queued_items = this.$('.xqueue');
this.num_queued_items = this.queued_items.length; this.num_queued_items = this.queued_items.length;
if (this.num_queued_items > 0) { if (this.num_queued_items > 0) {
if (window.queuePollerID) { if (window.queuePollerID) { // Only one poller 'thread' per Problem
window.clearTimeout(window.queuePollerID); window.clearTimeout(window.queuePollerID);
} }
return window.queuePollerID = window.setTimeout(function () { window.queuePollerID = window.setTimeout(function() {
return _this.poll(1000, focus_callback); return that.poll(1000, focusCallback);
}, 1000); }, 1000);
} }
}; };
Problem.prototype.poll = function (prev_timeout, focus_callback) { Problem.prototype.poll = function(previousTimeout, focusCallback) {
var _this = this; var that = this;
return $.postWithPrefix("" + this.url + "/problem_get", function (response) { return $.postWithPrefix('' + this.url + '/problem_get', function(response) {
var new_timeout; var newTimeout;
_this.new_queued_items = $(response.html).find(".xqueue"); // If queueing status changed, then render
if (_this.new_queued_items.length !== _this.num_queued_items) { that.new_queued_items = $(response.html).find('.xqueue');
edx.HtmlUtils.setHtml(_this.el, edx.HtmlUtils.HTML(response.html)).promise().done(function () { if (that.new_queued_items.length !== that.num_queued_items) {
return typeof focus_callback === "function" ? focus_callback() : void 0; edx.HtmlUtils.setHtml(that.el, edx.HtmlUtils.HTML(response.html)).promise().done(function() {
return typeof focusCallback === 'function' ? focusCallback() : void 0;
}); });
JavascriptLoader.executeModuleScripts(_this.el, function () { JavascriptLoader.executeModuleScripts(that.el, function() {
_this.setupInputTypes(); that.setupInputTypes();
return _this.bind(); that.bind();
}); });
} }
_this.num_queued_items = _this.new_queued_items.length; that.num_queued_items = that.new_queued_items.length;
if (_this.num_queued_items === 0) { if (that.num_queued_items === 0) {
_this.forceUpdate(response); that.forceUpdate(response);
return delete window.queuePollerID; delete window.queuePollerID;
} else { } else {
new_timeout = prev_timeout * 2; newTimeout = previousTimeout * 2;
if (new_timeout >= 60000) { // if the timeout is greather than 1 minute
if (newTimeout >= 60000) {
delete window.queuePollerID; delete window.queuePollerID;
return _this.gentle_alert(gettext("The grading process is still running. Refresh the page to see updates.")); that.gentle_alert(
gettext('The grading process is still running. Refresh the page to see updates.')
);
} else { } else {
return window.queuePollerID = window.setTimeout(function () { window.queuePollerID = window.setTimeout(function() {
return _this.poll(new_timeout, focus_callback); return that.poll(newTimeout, focusCallback);
}, new_timeout); }, newTimeout);
} }
} }
}); });
}; };
Problem.inputAjax = function (url, input_id, dispatch, data, callback) { /**
data['dispatch'] = dispatch; * Use this if you want to make an ajax call on the input type object
data['input_id'] = input_id; * static method so you don't have to instantiate a Problem in order to use it
return $.postWithPrefix("" + url + "/input_ajax", data, callback); *
* Input:
* url: the AJAX url of the problem
* inputId: the inputId of the input you would like to make the call on
* NOTE: the id is the ${id} part of "input_${id}" during rendering
* If this function is passed the entire prefixed id, the backend may have trouble
* finding the correct input
* dispatch: string that indicates how this data should be handled by the inputtype
* data: dictionary of data to send to the server
* callback: the function that will be called once the AJAX call has been completed.
* It will be passed a response object
*/
Problem.inputAjax = function(url, inputId, dispatch, data, callback) {
data.dispatch = dispatch; // eslint-disable-line no-param-reassign
data.input_id = inputId; // eslint-disable-line no-param-reassign
return $.postWithPrefix('' + url + '/input_ajax', data, callback);
}; };
Problem.prototype.render = function (content, focus_callback) { Problem.prototype.render = function(content, focusCallback) {
var _this = this; var that = this;
if (content) { if (content) {
this.el.html(content); this.el.html(content);
return JavascriptLoader.executeModuleScripts(this.el, function () { return JavascriptLoader.executeModuleScripts(this.el, function() {
_this.setupInputTypes(); that.setupInputTypes();
_this.bind(); that.bind();
_this.queueing(focus_callback); that.queueing(focusCallback);
_this.renderProgressState(); that.renderProgressState();
return typeof focus_callback === "function" ? focus_callback() : void 0; return typeof focusCallback === 'function' ? focusCallback() : void 0;
}); });
} else { } else {
return $.postWithPrefix("" + this.url + "/problem_get", function (response) { return $.postWithPrefix('' + this.url + '/problem_get', function(response) {
_this.el.html(response.html); that.el.html(response.html);
return JavascriptLoader.executeModuleScripts(_this.el, function () { return JavascriptLoader.executeModuleScripts(that.el, function() {
_this.setupInputTypes(); that.setupInputTypes();
_this.bind(); that.bind();
_this.queueing(); that.queueing();
return _this.forceUpdate(response); return that.forceUpdate(response);
}); });
}); });
} }
}; };
Problem.prototype.setupInputTypes = function () { Problem.prototype.setupInputTypes = function() {
var _this = this; var that = this;
this.inputtypeDisplays = {}; this.inputtypeDisplays = {};
return this.el.find(".capa_inputtype").each(function (index, inputtype) { return this.el.find('.capa_inputtype').each(function(index, inputtype) {
var classes, cls, id, setupMethod, _i, _len, _results; var classes, cls, id, setupMethod, i, len, results;
classes = $(inputtype).attr('class').split(' '); classes = $(inputtype).attr('class').split(' ');
id = $(inputtype).attr('id'); id = $(inputtype).attr('id');
_results = []; results = [];
for (_i = 0, _len = classes.length; _i < _len; _i++) { for (i = 0, len = classes.length; i < len; i++) {
cls = classes[_i]; cls = classes[i];
setupMethod = _this.inputtypeSetupMethods[cls]; setupMethod = that.inputtypeSetupMethods[cls];
if (setupMethod != null) { if (setupMethod != null) {
_results.push(_this.inputtypeDisplays[id] = setupMethod(inputtype)); results.push(that.inputtypeDisplays[id] = setupMethod(inputtype));
} else { } else {
_results.push(void 0); results.push(void 0);
} }
} }
return _results; return results;
}); });
}; };
Problem.prototype.submit_save_waitfor = function (callback) { /**
var flag, inp, _i, _len, _ref, * If some function wants to be called before sending the answer to the
_this = this; * server, give it a chance to do so.
*
* submit_save_waitfor allows the callee to send alerts if the user's input is
* invalid. To do so, the callee must throw an exception named "WaitforException".
* This and any other errors or exceptions that arise from the callee are rethrown
* and abort the submission.
*
* In order to use this feature, add a 'data-waitfor' attribute to the input,
* and specify the function to be called by the submit button before sending off @answers
*/
Problem.prototype.submit_save_waitfor = function(callback) {
var flag, inp, i, len, ref,
that = this;
flag = false; flag = false;
_ref = this.inputs; ref = this.inputs;
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (i = 0, len = ref.length; i < len; i++) {
inp = _ref[_i]; inp = ref[i];
if ($(inp).is("input[waitfor]")) { if ($(inp).is('input[waitfor]')) {
try { try {
$(inp).data("waitfor")(function () { $(inp).data('waitfor')(function() {
_this.refreshAnswers(); that.refreshAnswers();
return callback(); return callback();
}); });
} catch (e) { } catch (e) {
if (e.name === "Waitfor Exception") { if (e.name === 'Waitfor Exception') {
alert(e.message); alert(e.message); // eslint-disable-line no-alert
} else { } else {
alert("Could not grade your answer. The submission was aborted."); alert( // eslint-disable-line no-alert
gettext('Could not grade your answer. The submission was aborted.')
);
} }
throw e; throw e;
} }
...@@ -377,406 +450,464 @@ ...@@ -377,406 +450,464 @@
return flag; return flag;
}; };
Problem.prototype.scroll_to_problem_meta = function () { // Scroll to problem metadata and next focus is problem input
Problem.prototype.scroll_to_problem_meta = function() {
var questionTitle; var questionTitle;
questionTitle = this.$(".problem-header"); questionTitle = this.$('.problem-header');
if (questionTitle.length > 0) { if (questionTitle.length > 0) {
$('html, body').animate({ $('html, body').animate({
scrollTop: questionTitle.offset().top scrollTop: questionTitle.offset().top
}, 500); }, 500);
return questionTitle.focus(); questionTitle.focus();
} }
}; };
Problem.prototype.focus_on_notification = function (type) { Problem.prototype.focus_on_notification = function(type) {
var notification; var notification;
notification = this.$('.notification-' + type); notification = this.$('.notification-' + type);
if (notification.length > 0) { if (notification.length > 0) {
return notification.focus(); notification.focus();
} }
}; };
Problem.prototype.focus_on_submit_notification = function () { Problem.prototype.focus_on_submit_notification = function() {
return this.focus_on_notification('submit'); this.focus_on_notification('submit');
}; };
Problem.prototype.focus_on_hint_notification = function () { Problem.prototype.focus_on_hint_notification = function() {
return this.focus_on_notification('hint'); this.focus_on_notification('hint');
}; };
Problem.prototype.focus_on_save_notification = function () { Problem.prototype.focus_on_save_notification = function() {
return this.focus_on_notification('save'); this.focus_on_notification('save');
}; };
/* /**
# 'submit_fd' uses FormData to allow file submissions in the 'problem_check' dispatch, * 'submit_fd' uses FormData to allow file submissions in the 'problem_check' dispatch,
# in addition to simple querystring-based answers * in addition to simple querystring-based answers
# *
# NOTE: The dispatch 'problem_check' is being singled out for the use of FormData; * NOTE: The dispatch 'problem_check' is being singled out for the use of FormData;
# maybe preferable to consolidate all dispatches to use FormData * maybe preferable to consolidate all dispatches to use FormData
*/ */
Problem.prototype.submit_fd = function() {
var abortSubmission, error, errorHtml, errors, fd, fileNotSelected, fileTooLarge, maxFileSize,
requiredFilesNotSubmitted, settings, timeoutId, unallowedFileSubmitted, i, len,
that = this;
// If there are no file inputs in the problem, we can fall back on submit.
Problem.prototype.submit_fd = function () {
var abort_submission, error, error_html, errors, fd, file_not_selected, file_too_large, max_filesize, required_files_not_submitted, settings, timeout_id, unallowed_file_submitted, _i, _len,
_this = this;
if (this.el.find('input:file').length === 0) { if (this.el.find('input:file').length === 0) {
this.submit(); this.submit();
return; return;
} }
this.enableSubmitButton(false); this.enableSubmitButton(false);
if (!window.FormData) { if (!window.FormData) {
alert("Submission aborted! Sorry, your browser does not support file uploads. If you can, please use Chrome or Safari which have been verified to support file uploads."); alert(gettext('Submission aborted! Sorry, your browser does not support file uploads. If you can, please use Chrome or Safari which have been verified to support file uploads.')); // eslint-disable-line max-len, no-alert
this.enableSubmitButton(true); this.enableSubmitButton(true);
return; return;
} }
timeout_id = this.enableSubmitButtonAfterTimeout(); timeoutId = this.enableSubmitButtonAfterTimeout();
fd = new FormData(); fd = new FormData();
max_filesize = 4 * 1000 * 1000;
file_too_large = false; // Sanity checks on submission
file_not_selected = false; maxFileSize = 4 * 1000 * 1000;
required_files_not_submitted = false; fileTooLarge = false;
unallowed_file_submitted = false; fileNotSelected = false;
requiredFilesNotSubmitted = false;
unallowedFileSubmitted = false;
errors = []; errors = [];
this.inputs.each(function (index, element) { this.inputs.each(function(index, element) {
var allowed_files, file, max_size, required_files, _i, _len, _ref, _ref1, _ref2; var allowedFiles, file, maxSize, requiredFiles, loopI, loopLen, ref;
if (element.type === 'file') { if (element.type === 'file') {
required_files = $(element).data("required_files"); requiredFiles = $(element).data('required_files');
allowed_files = $(element).data("allowed_files"); allowedFiles = $(element).data('allowed_files');
_ref = element.files; ref = element.files;
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (loopI = 0, loopLen = ref.length; loopI < loopLen; loopI++) {
file = _ref[_i]; file = ref[loopI];
if (allowed_files.length !== 0 && (_ref1 = file.name, __indexOf.call(allowed_files, _ref1) < 0)) { if (allowedFiles.length !== 0 && indexOfHelper.call(allowedFiles, file.name < 0)) {
unallowed_file_submitted = true; unallowedFileSubmitted = true;
errors.push("You submitted " + file.name + "; only " + allowed_files + " are allowed."); errors.push(edx.StringUtils.interpolate(
gettext('You submitted {filename}; only {allowedFiles} are allowed.'), {
filename: file.name,
allowedFiles: allowedFiles
}
));
} }
if (_ref2 = file.name, __indexOf.call(required_files, _ref2) >= 0) { if (indexOfHelper.call(requiredFiles, file.name) >= 0) {
required_files.splice(required_files.indexOf(file.name), 1); requiredFiles.splice(requiredFiles.indexOf(file.name), 1);
} }
if (file.size > max_filesize) { if (file.size > maxFileSize) {
file_too_large = true; fileTooLarge = true;
max_size = max_filesize / (1000 * 1000); maxSize = maxFileSize / (1000 * 1000);
errors.push("Your file " + file.name + " is too large (max size: {max_size}MB)"); errors.push(edx.StringUtils.interpolate(
gettext('Your file {filename} is too large (max size: {maxSize}MB).'), {
filename: file.name,
maxSize: maxSize
}
));
} }
fd.append(element.id, file); fd.append(element.id, file);
} }
if (element.files.length === 0) { if (element.files.length === 0) {
file_not_selected = true; fileNotSelected = true;
fd.append(element.id, ''); fd.append(element.id, ''); // In case we want to allow submissions with no file
} }
if (required_files.length !== 0) { if (requiredFiles.length !== 0) {
required_files_not_submitted = true; requiredFilesNotSubmitted = true;
return errors.push("You did not submit the required files: " + required_files + "."); errors.push(edx.StringUtils.interpolate(
gettext('You did not submit the required files: {requiredFiles}.'), {
requiredFiles: requiredFiles
}
));
} }
} else { } else {
return fd.append(element.id, element.value); fd.append(element.id, element.value);
} }
}); });
if (file_not_selected) { if (fileNotSelected) {
errors.push('You did not select any files to submit'); errors.push(gettext('You did not select any files to submit.'));
} }
error_html = '<ul>\n'; errorHtml = '<ul>\n';
for (_i = 0, _len = errors.length; _i < _len; _i++) { for (i = 0, len = errors.length; i < len; i++) {
error = errors[_i]; error = errors[i];
error_html += '<li>' + error + '</li>\n'; errorHtml += '<li>' + error + '</li>\n';
} }
error_html += '</ul>'; errorHtml += '</ul>';
this.gentle_alert(error_html); this.gentle_alert(errorHtml);
abort_submission = file_too_large || file_not_selected || unallowed_file_submitted || required_files_not_submitted; abortSubmission = fileTooLarge || fileNotSelected || unallowedFileSubmitted || requiredFilesNotSubmitted;
if (abort_submission) { if (abortSubmission) {
window.clearTimeout(timeout_id); window.clearTimeout(timeoutId);
this.enableSubmitButton(true); this.enableSubmitButton(true);
return; } else {
} settings = {
settings = { type: 'POST',
type: "POST", data: fd,
data: fd, processData: false,
processData: false, contentType: false,
contentType: false, complete: this.enableSubmitButtonAfterResponse,
complete: this.enableSubmitButtonAfterResponse, success: function(response) {
success: function (response) { switch (response.success) {
switch (response.success) {
case 'incorrect': case 'incorrect':
case 'correct': case 'correct':
_this.render(response.contents); that.render(response.contents);
_this.updateProgress(response); that.updateProgress(response);
break; break;
default: default:
_this.gentle_alert(response.success); that.gentle_alert(response.success);
}
return Logger.log('problem_graded', [that.answers, response.contents], that.id);
} }
return Logger.log('problem_graded', [_this.answers, response.contents], _this.id); };
} $.ajaxWithPrefix('' + this.url + '/problem_check', settings);
}; }
return $.ajaxWithPrefix("" + this.url + "/problem_check", settings);
}; };
Problem.prototype.submit = function () { Problem.prototype.submit = function() {
if (!this.submit_save_waitfor(this.submit_internal)) { if (!this.submit_save_waitfor(this.submit_internal)) {
return this.disableAllButtonsWhileRunning(this.submit_internal, true); this.disableAllButtonsWhileRunning(this.submit_internal, true);
} }
}; };
Problem.prototype.submit_internal = function () { Problem.prototype.submit_internal = function() {
var _this = this; var that = this;
Logger.log('problem_check', this.answers); Logger.log('problem_check', this.answers);
return $.postWithPrefix("" + this.url + "/problem_check", this.answers, function (response) { return $.postWithPrefix('' + this.url + '/problem_check', this.answers, function(response) {
switch (response.success) { switch (response.success) {
case 'incorrect': case 'incorrect':
case 'correct': case 'correct':
window.SR.readTexts(_this.get_sr_status(response.contents)); window.SR.readTexts(that.get_sr_status(response.contents));
_this.el.trigger('contentChanged', [_this.id, response.contents]); that.el.trigger('contentChanged', [that.id, response.contents]);
_this.render(response.contents, _this.focus_on_submit_notification); that.render(response.contents, that.focus_on_submit_notification);
_this.updateProgress(response); that.updateProgress(response);
break; break;
default: default:
_this.saveNotification.hide(); that.saveNotification.hide();
_this.gentle_alert(response.success); that.gentle_alert(response.success);
} }
return Logger.log('problem_graded', [_this.answers, response.contents], _this.id); return Logger.log('problem_graded', [that.answers, response.contents], that.id);
}); });
}; };
Problem.prototype.get_sr_status = function (contents) { /**
var added_status, aria_label, element, labeled_status, parent_section, status_elements, template, _i, _len; * This method builds up an array of strings to send to the page screen-reader span.
status_elements = $(contents).find('.status'); * It first gets all elements with class "status", and then looks to see if they are contained
labeled_status = []; * in sections with aria-labels. If so, labels are prepended to the status element text.
for (_i = 0, _len = status_elements.length; _i < _len; _i++) { * If not, just the text of the status elements are returned.
element = status_elements[_i]; */
parent_section = $(element).closest('section'); Problem.prototype.get_sr_status = function(contents) {
added_status = false; var addedStatus, ariaLabel, element, labeledStatus, parentSection, statusElement, template, i, len;
if (parent_section) { statusElement = $(contents).find('.status');
aria_label = parent_section.attr('aria-label'); labeledStatus = [];
if (aria_label) { for (i = 0, len = statusElement.length; i < len; i++) {
// Translators: This is only translated to allow for reording of label and associated status.; element = statusElement[i];
template = gettext("{label}: {status}"); parentSection = $(element).closest('section');
labeled_status.push(edx.StringUtils.interpolate(template, { addedStatus = false;
label: aria_label, if (parentSection) {
status: $(element).text() ariaLabel = parentSection.attr('aria-label');
})); if (ariaLabel) {
added_status = true; // Translators: This is only translated to allow for reordering of label and associated status.;
template = gettext('{label}: {status}');
labeledStatus.push(edx.StringUtils.interpolate(
template, {
label: ariaLabel,
status: $(element).text()
}
));
addedStatus = true;
} }
} }
if (!added_status) { if (!addedStatus) {
labeled_status.push($(element).text()); labeledStatus.push($(element).text());
} }
} }
return labeled_status; return labeledStatus;
}; };
Problem.prototype.reset = function () { Problem.prototype.reset = function() {
return this.disableAllButtonsWhileRunning(this.reset_internal, false); return this.disableAllButtonsWhileRunning(this.reset_internal, false);
}; };
Problem.prototype.reset_internal = function () { Problem.prototype.reset_internal = function() {
var _this = this; var that = this;
Logger.log('problem_reset', this.answers); Logger.log('problem_reset', this.answers);
return $.postWithPrefix("" + this.url + "/problem_reset", { return $.postWithPrefix('' + this.url + '/problem_reset', {
id: this.id id: this.id
}, function (response) { }, function(response) {
if (response.success) { if (response.success) {
_this.el.trigger('contentChanged', [_this.id, response.html]); that.el.trigger('contentChanged', [that.id, response.html]);
_this.render(response.html, _this.scroll_to_problem_meta); that.render(response.html, that.scroll_to_problem_meta);
_this.updateProgress(response); that.updateProgress(response);
return window.SR.readText(gettext('This problem has been reset.')); return window.SR.readText(gettext('This problem has been reset.'));
} else { } else {
return _this.gentle_alert(response.msg); return that.gentle_alert(response.msg);
} }
}); });
}; };
Problem.prototype.show = function () { // TODO this needs modification to deal with javascript responses; perhaps we
var _this = this; // need something where responsetypes can define their own behavior when show
// is called.
Problem.prototype.show = function() {
var that = this;
Logger.log('problem_show', { Logger.log('problem_show', {
problem: this.id problem: this.id
}); });
return $.postWithPrefix("" + this.url + "/problem_show", function (response) { return $.postWithPrefix('' + this.url + '/problem_show', function(response) {
var answers; var answers;
answers = response.answers; answers = response.answers;
$.each(answers, function (key, value) { $.each(answers, function(key, value) {
var answer, choice, solution, _i, _len, _results; var answer, choice, i, len, results;
if ($.isArray(value)) { if ($.isArray(value)) {
_results = []; results = [];
for (_i = 0, _len = value.length; _i < _len; _i++) { for (i = 0, len = value.length; i < len; i++) {
choice = value[_i]; choice = value[i];
_results.push(_this.$("label[for='input_" + key + "_" + choice + "']").attr({ results.push(that.$('label[for="input_' + key + '_' + choice + '"]').attr({
correct_answer: 'true' correct_answer: 'true'
})); }));
} }
return _results; return results;
} else { } else {
answer = _this.$("#answer_" + key + ", #solution_" + key); answer = that.$('#answer_' + key + ', #solution_' + key);
edx.HtmlUtils.setHtml(answer, edx.HtmlUtils.HTML(value)); edx.HtmlUtils.setHtml(answer, edx.HtmlUtils.HTML(value));
Collapsible.setCollapsibles(answer); Collapsible.setCollapsibles(answer);
// Sometimes, `value` is just a string containing a MathJax formula.
// If this is the case, jQuery will throw an error in some corner cases
// because of an incorrect selector. We setup a try..catch so that
// the script doesn't break in such cases.
//
// We will fallback to the second `if statement` below, if an
// error is thrown by jQuery.
try { try {
return solution = $(value).find('.detailed-solution'); return $(value).find('.detailed-solution');
} catch (e) { } catch (e) {
return solution = {}; return {};
} }
// TODO remove the above once everything is extracted into its own
// inputtype functions.
} }
}); });
_this.el.find(".capa_inputtype").each(function (index, inputtype) { that.el.find('.capa_inputtype').each(function(index, inputtype) {
var classes, cls, display, showMethod, _i, _len, _results; var classes, cls, display, showMethod, i, len, results;
classes = $(inputtype).attr('class').split(' '); classes = $(inputtype).attr('class').split(' ');
_results = []; results = [];
for (_i = 0, _len = classes.length; _i < _len; _i++) { for (i = 0, len = classes.length; i < len; i++) {
cls = classes[_i]; cls = classes[i];
display = _this.inputtypeDisplays[$(inputtype).attr('id')]; display = that.inputtypeDisplays[$(inputtype).attr('id')];
showMethod = _this.inputtypeShowAnswerMethods[cls]; showMethod = that.inputtypeShowAnswerMethods[cls];
if (showMethod != null) { if (showMethod != null) {
_results.push(showMethod(inputtype, display, answers)); results.push(showMethod(inputtype, display, answers));
} else { } else {
_results.push(void 0); results.push(void 0);
} }
} }
return _results; return results;
}); });
if (typeof MathJax !== "undefined" && MathJax !== null) { if (typeof MathJax !== 'undefined' && MathJax !== null) {
_this.el.find('.problem > div').each(function (index, element) { that.el.find('.problem > div').each(function(index, element) {
return MathJax.Hub.Queue(["Typeset", MathJax.Hub, element]); return MathJax.Hub.Queue(['Typeset', MathJax.Hub, element]);
}); });
} }
_this.el.find('.show').attr('disabled', 'disabled'); that.el.find('.show').attr('disabled', 'disabled');
_this.updateProgress(response); that.updateProgress(response);
window.SR.readText(gettext('Answers to this problem are now shown. Navigate through the problem to review it with answers inline.')); window.SR.readText(gettext('Answers to this problem are now shown. Navigate through the problem to review it with answers inline.')); // eslint-disable-line max-len
return _this.scroll_to_problem_meta(); that.scroll_to_problem_meta();
}); });
}; };
Problem.prototype.clear_all_notifications = function () { Problem.prototype.clear_all_notifications = function() {
this.submitNotification.remove(); this.submitNotification.remove();
this.gentleAlertNotification.hide(); this.gentleAlertNotification.hide();
return this.saveNotification.hide(); this.saveNotification.hide();
}; };
Problem.prototype.gentle_alert = function (msg) { Problem.prototype.gentle_alert = function(msg) {
edx.HtmlUtils.setHtml(this.el.find('.notification-gentle-alert .notification-message'), edx.HtmlUtils.HTML(msg)); edx.HtmlUtils.setHtml(
this.el.find('.notification-gentle-alert .notification-message'),
edx.HtmlUtils.HTML(msg)
);
this.clear_all_notifications(); this.clear_all_notifications();
this.gentleAlertNotification.show(); this.gentleAlertNotification.show();
return this.gentleAlertNotification.focus(); this.gentleAlertNotification.focus();
}; };
Problem.prototype.save = function () { Problem.prototype.save = function() {
if (!this.submit_save_waitfor(this.save_internal)) { if (!this.submit_save_waitfor(this.save_internal)) {
return this.disableAllButtonsWhileRunning(this.save_internal, false); this.disableAllButtonsWhileRunning(this.save_internal, false);
} }
}; };
Problem.prototype.save_internal = function () { Problem.prototype.save_internal = function() {
var _this = this; var that = this;
Logger.log('problem_save', this.answers); Logger.log('problem_save', this.answers);
return $.postWithPrefix("" + this.url + "/problem_save", this.answers, function (response) { return $.postWithPrefix('' + this.url + '/problem_save', this.answers, function(response) {
var saveMessage; var saveMessage;
saveMessage = response.msg; saveMessage = response.msg;
if (response.success) { if (response.success) {
_this.el.trigger('contentChanged', [_this.id, response.html]); that.el.trigger('contentChanged', [that.id, response.html]);
edx.HtmlUtils.setHtml(_this.el.find('.notification-save .notification-message'), edx.HtmlUtils.HTML(saveMessage)); edx.HtmlUtils.setHtml(
_this.clear_all_notifications(); that.el.find('.notification-save .notification-message'),
_this.saveNotification.show(); edx.HtmlUtils.HTML(saveMessage)
return _this.focus_on_save_notification(); );
that.clear_all_notifications();
that.saveNotification.show();
that.focus_on_save_notification();
} else { } else {
return _this.gentle_alert(saveMessage); that.gentle_alert(saveMessage);
} }
}); });
}; };
Problem.prototype.refreshMath = function (event, element) { Problem.prototype.refreshMath = function(event, element) {
var elid, eqn, jax, mathjax_preprocessor, preprocessor_tag, target; var elid, eqn, jax, mathjaxPreprocessor, preprocessorTag, target;
if (!element) { if (!element) {
element = event.target; element = event.target; // eslint-disable-line no-param-reassign
} }
elid = element.id.replace(/^input_/, ''); elid = element.id.replace(/^input_/, '');
target = "display_" + elid; target = 'display_' + elid;
preprocessor_tag = "inputtype_" + elid;
mathjax_preprocessor = this.inputtypeDisplays[preprocessor_tag]; // MathJax preprocessor is loaded by 'setupInputTypes'
if ((typeof MathJax !== "undefined" && MathJax !== null) && (jax = MathJax.Hub.getAllJax(target)[0])) { preprocessorTag = 'inputtype_' + elid;
mathjaxPreprocessor = this.inputtypeDisplays[preprocessorTag];
if (typeof MathJax !== 'undefined' && MathJax !== null && MathJax.Hub.getAllJax(target)[0]) {
jax = MathJax.Hub.getAllJax(target)[0];
eqn = $(element).val(); eqn = $(element).val();
if (mathjax_preprocessor) { if (mathjaxPreprocessor) {
eqn = mathjax_preprocessor(eqn); eqn = mathjaxPreprocessor(eqn);
} }
MathJax.Hub.Queue(['Text', jax, eqn], [this.updateMathML, jax, element]); MathJax.Hub.Queue(['Text', jax, eqn], [this.updateMathML, jax, element]);
} }
}; };
Problem.prototype.updateMathML = function (jax, element) { Problem.prototype.updateMathML = function(jax, element) {
try { try {
return $("#" + element.id + "_dynamath").val(jax.root.toMathML('')); $('#' + element.id + '_dynamath').val(jax.root.toMathML(''));
} catch (exception) { } catch (exception) {
if (!exception.restart) { if (!exception.restart) {
throw exception; throw exception;
} }
if (typeof MathJax !== "undefined" && MathJax !== null) { if (typeof MathJax !== 'undefined' && MathJax !== null) {
return MathJax.Callback.After([this.refreshMath, jax], exception.restart); MathJax.Callback.After([this.refreshMath, jax], exception.restart);
} }
} }
}; };
Problem.prototype.refreshAnswers = function () { Problem.prototype.refreshAnswers = function() {
this.$('input.schematic').each(function (index, element) { this.$('input.schematic').each(function(index, element) {
return element.schematic.update_value(); return element.schematic.update_value();
}); });
this.$(".CodeMirror").each(function (index, element) { this.$('.CodeMirror').each(function(index, element) {
if (element.CodeMirror.save) { if (element.CodeMirror.save) {
return element.CodeMirror.save(); element.CodeMirror.save();
} }
}); });
return this.answers = this.inputs.serialize(); this.answers = this.inputs.serialize();
}; };
Problem.prototype.submitAnswersAndSubmitButton = function (bind) { /**
var answered, at_least_one_text_input_found, one_text_input_filled, * Used to check available answers and if something is checked (or the answer is set in some textbox),
_this = this; * the "Submit" button becomes enabled. Otherwise it is disabled by default.
if (bind == null) { *
bind = false; * Arguments:
* bind (boolean): used on the first check to attach event handlers to input fields
* to change "Submit" enable status in case of some manipulations with answers
*/
Problem.prototype.submitAnswersAndSubmitButton = function(bind) {
var answered, atLeastOneTextInputFound, oneTextInputFilled,
that = this;
if (bind === null || bind === undefined) {
bind = false; // eslint-disable-line no-param-reassign
} }
"Used to check available answers and if something is checked (or the answer is set in some textbox)\n\"Submit\" button becomes enabled. Otherwise it is disabled by default.\n\nArguments:\n bind (bool): used on the first check to attach event handlers to input fields\n to change \"Submit\" enable status in case of some manipulations with answers";
answered = true; answered = true;
at_least_one_text_input_found = false; atLeastOneTextInputFound = false;
one_text_input_filled = false; oneTextInputFilled = false;
this.el.find("input:text").each(function (i, text_field) { this.el.find('input:text').each(function(i, textField) {
if ($(text_field).is(':visible')) { if ($(textField).is(':visible')) {
at_least_one_text_input_found = true; atLeastOneTextInputFound = true;
if ($(text_field).val() !== '') { if ($(textField).val() !== '') {
one_text_input_filled = true; oneTextInputFilled = true;
} }
if (bind) { if (bind) {
$(text_field).on('input', function (e) { $(textField).on('input', function() {
_this.saveNotification.hide(); that.saveNotification.hide();
_this.submitAnswersAndSubmitButton(); that.submitAnswersAndSubmitButton();
}); });
} }
} }
}); });
if (at_least_one_text_input_found && !one_text_input_filled) { if (atLeastOneTextInputFound && !oneTextInputFilled) {
answered = false; answered = false;
} }
this.el.find(".choicegroup").each(function (i, choicegroup_block) { this.el.find('.choicegroup').each(function(i, choicegroupBlock) {
var checked; var checked;
checked = false; checked = false;
$(choicegroup_block).find("input[type=checkbox], input[type=radio]").each(function (j, checkbox_or_radio) { $(choicegroupBlock).find('input[type=checkbox], input[type=radio]').
if ($(checkbox_or_radio).is(':checked')) { each(function(j, checkboxOrRadio) {
checked = true; if ($(checkboxOrRadio).is(':checked')) {
} checked = true;
if (bind) { }
$(checkbox_or_radio).on('click', function (e) { if (bind) {
_this.saveNotification.hide(); $(checkboxOrRadio).on('click', function() {
_this.submitAnswersAndSubmitButton(); that.saveNotification.hide();
}); that.submitAnswersAndSubmitButton();
} });
}); }
});
if (!checked) { if (!checked) {
answered = false; answered = false;
} }
}); });
this.el.find("select").each(function (i, select_field) { this.el.find('select').each(function(i, selectField) {
var selected_option; var selectedOption = $(selectField).find('option:selected').text()
selected_option = $(select_field).find("option:selected").text().trim(); .trim();
if (selected_option === 'Select an option') { if (selectedOption === 'Select an option') {
answered = false; answered = false;
} }
if (bind) { if (bind) {
$(select_field).on('change', function (e) { $(selectField).on('change', function() {
_this.saveNotification.hide(); that.saveNotification.hide();
_this.submitAnswersAndSubmitButton(); that.submitAnswersAndSubmitButton();
}); });
} }
}); });
...@@ -787,82 +918,86 @@ ...@@ -787,82 +918,86 @@
} }
}; };
Problem.prototype.bindResetCorrectness = function () { Problem.prototype.bindResetCorrectness = function() {
// Loop through all input types.
// Bind the reset functions at that scope.
var $inputtypes, var $inputtypes,
_this = this; that = this;
$inputtypes = this.el.find(".capa_inputtype").add(this.el.find(".inputtype")); $inputtypes = this.el.find('.capa_inputtype').add(this.el.find('.inputtype'));
return $inputtypes.each(function (index, inputtype) { return $inputtypes.each(function(index, inputtype) {
var bindMethod, classes, cls, _i, _len, _results; var bindMethod, classes, cls, i, len, results;
classes = $(inputtype).attr('class').split(' '); classes = $(inputtype).attr('class').split(' ');
_results = []; results = [];
for (_i = 0, _len = classes.length; _i < _len; _i++) { for (i = 0, len = classes.length; i < len; i++) {
cls = classes[_i]; cls = classes[i];
bindMethod = _this.bindResetCorrectnessByInputtype[cls]; bindMethod = that.bindResetCorrectnessByInputtype[cls];
if (bindMethod != null) { if (bindMethod != null) {
_results.push(bindMethod(inputtype)); results.push(bindMethod(inputtype));
} else { } else {
_results.push(void 0); results.push(void 0);
} }
} }
return _results; return results;
}); });
}; };
// Find all places where each input type displays its correct-ness
// Replace them with their original state--'unanswered'.
Problem.prototype.bindResetCorrectnessByInputtype = { Problem.prototype.bindResetCorrectnessByInputtype = {
formulaequationinput: function (element) { // These are run at the scope of the capa inputtype
return $(element).find('input').on('input', function () { // They should set handlers on each <input> to reset the whole.
formulaequationinput: function(element) {
return $(element).find('input').on('input', function() {
var $p; var $p;
$p = $(element).find('span.status'); $p = $(element).find('span.status');
// Translators: the word unanswered here is about answering a problem the student must solve.; return $p.parent().removeClass().addClass('unsubmitted');
return $p.parent().removeClass().addClass("unsubmitted");
}); });
}, },
choicegroup: function (element) { choicegroup: function(element) {
var $element, id; var $element, id;
$element = $(element); $element = $(element);
id = ($element.attr('id').match(/^inputtype_(.*)$/))[1]; id = ($element.attr('id').match(/^inputtype_(.*)$/))[1];
return $element.find('input').on('change', function () { return $element.find('input').on('change', function() {
var $status; var $status;
$status = $("#status_" + id); $status = $('#status_' + id);
if ($status[0]) { if ($status[0]) {
$status.removeClass().addClass("unanswered"); $status.removeClass().addClass('unanswered');
$status.empty().css('display', 'inline-block'); $status.empty().css('display', 'inline-block');
} else { } else {
$("<span>", { $('<span>', {
"class": "unanswered", class: 'unanswered',
"style": "display: inline-block;", style: 'display: inline-block;',
"id": "status_" + id id: 'status_' + id
}); });
} }
return $element.find("label").removeClass(); return $element.find('label').removeClass();
}); });
}, },
'option-input': function (element) { 'option-input': function(element) {
var $select, id; var $select, id;
$select = $(element).find('select'); $select = $(element).find('select');
id = ($select.attr('id').match(/^input_(.*)$/))[1]; id = ($select.attr('id').match(/^input_(.*)$/))[1];
return $select.on('change', function () { return $select.on('change', function() {
var $status; return $('#status_' + id).removeClass().addClass('unanswered')
return $status = $("#status_" + id).removeClass().addClass("unanswered").find('span').text(gettext('Status: unsubmitted')); .find('span')
.text(gettext('Status: unsubmitted'));
}); });
}, },
textline: function (element) { textline: function(element) {
return $(element).find('input').on('input', function () { return $(element).find('input').on('input', function() {
var $p; var $p;
$p = $(element).find('span.status'); $p = $(element).find('span.status');
// Translators: the word unanswered here is about answering a problem the student must solve.; return $p.parent().removeClass('correct incorrect').addClass('unsubmitted');
return $p.parent().removeClass("correct incorrect").addClass("unsubmitted");
}); });
} }
}; };
Problem.prototype.inputtypeSetupMethods = { Problem.prototype.inputtypeSetupMethods = {
'text-input-dynamath': function (element) { 'text-input-dynamath': function(element) {
/* /*
Return: function (eqn) -> eqn that preprocesses the user formula input before Return: function (eqn) -> eqn that preprocesses the user formula input before
it is fed into MathJax. Return 'false' if no preprocessor specified it is fed into MathJax. Return 'false' if no preprocessor specified
*/ */
var data, preprocessor, preprocessorClass, preprocessorClassName; var data, preprocessor, preprocessorClass, preprocessorClassName;
data = $(element).find('.text-input-dynamath_data'); data = $(element).find('.text-input-dynamath_data');
preprocessorClassName = data.data('preprocessor'); preprocessorClassName = data.data('preprocessor');
...@@ -874,30 +1009,31 @@ ...@@ -874,30 +1009,31 @@
return preprocessor.fn; return preprocessor.fn;
} }
}, },
javascriptinput: function (element) { javascriptinput: function(element) {
var container, data, display, displayClass, evaluation, params, problemState, submission, submissionField; var container, data, display, displayClass, evaluation, params, problemState, submission,
data = $(element).find(".javascriptinput_data"); submissionField;
params = data.data("params"); data = $(element).find('.javascriptinput_data');
submission = data.data("submission"); params = data.data('params');
evaluation = data.data("evaluation"); submission = data.data('submission');
problemState = data.data("problem_state"); evaluation = data.data('evaluation');
problemState = data.data('problem_state');
displayClass = window[data.data('display_class')]; displayClass = window[data.data('display_class')];
if (evaluation === '') { if (evaluation === '') {
evaluation = null; evaluation = null;
} }
container = $(element).find(".javascriptinput_container"); container = $(element).find('.javascriptinput_container');
submissionField = $(element).find(".javascriptinput_input"); submissionField = $(element).find('.javascriptinput_input');
display = new displayClass(problemState, submission, evaluation, container, submissionField, params); display = new displayClass(problemState, submission, evaluation, container, submissionField, params);
display.render(); display.render();
return display; return display;
}, },
cminput: function (container) { cminput: function(container) {
var CodeMirrorEditor, CodeMirrorTextArea, element, id, linenumbers, mode, spaces, tabsize; var CodeMirrorEditor, CodeMirrorTextArea, element, id, linenumbers, mode, spaces, tabsize;
element = $(container).find("textarea"); element = $(container).find('textarea');
tabsize = element.data("tabsize"); tabsize = element.data('tabsize');
mode = element.data("mode"); mode = element.data('mode');
linenumbers = element.data("linenums"); linenumbers = element.data('linenums');
spaces = Array(parseInt(tabsize) + 1).join(" "); spaces = Array(parseInt(tabsize, 10) + 1).join(' ');
CodeMirrorEditor = CodeMirror.fromTextArea(element[0], { CodeMirrorEditor = CodeMirror.fromTextArea(element[0], {
lineNumbers: linenumbers, lineNumbers: linenumbers,
indentUnit: tabsize, indentUnit: tabsize,
...@@ -908,89 +1044,108 @@ ...@@ -908,89 +1044,108 @@
indentWithTabs: false, indentWithTabs: false,
smartIndent: false, smartIndent: false,
extraKeys: { extraKeys: {
"Esc": function (cm) { Esc: function() {
$(".grader-status").focus(); $('.grader-status').focus();
return false; return false;
}, },
"Tab": function (cm) { Tab: function(cm) {
cm.replaceSelection(spaces, "end"); cm.replaceSelection(spaces, 'end');
return false; return false;
} }
} }
}); });
id = element.attr("id").replace(/^input_/, ""); id = element.attr('id').replace(/^input_/, '');
CodeMirrorTextArea = CodeMirrorEditor.getInputField(); CodeMirrorTextArea = CodeMirrorEditor.getInputField();
CodeMirrorTextArea.setAttribute("id", "cm-textarea-" + id); CodeMirrorTextArea.setAttribute('id', 'cm-textarea-' + id);
CodeMirrorTextArea.setAttribute("aria-describedby", "cm-editor-exit-message-" + id + " status_" + id); CodeMirrorTextArea.setAttribute('aria-describedby', 'cm-editor-exit-message-' + id + ' status_' + id);
return CodeMirrorEditor; return CodeMirrorEditor;
} }
}; };
Problem.prototype.inputtypeShowAnswerMethods = { Problem.prototype.inputtypeShowAnswerMethods = {
choicegroup: function (element, display, answers) { choicegroup: function(element, display, answers) {
var answer, choice, input_id, _i, _len, _results; var answer, choice, inputId, i, len, results, $element;
element = $(element); $element = $(element);
input_id = element.attr('id').replace(/inputtype_/, ''); inputId = $element.attr('id').replace(/inputtype_/, '');
answer = answers[input_id]; answer = answers[inputId];
_results = []; results = [];
for (_i = 0, _len = answer.length; _i < _len; _i++) { for (i = 0, len = answer.length; i < len; i++) {
choice = answer[_i]; choice = answer[i];
_results.push(element.find("#input_" + input_id + "_" + choice).parent("label").addClass('choicegroup_correct')); results.push($element.find('#input_' + inputId + '_' + choice).parent('label').
addClass('choicegroup_correct'));
} }
return _results; return results;
}, },
javascriptinput: function (element, display, answers) { javascriptinput: function(element, display, answers) {
var answer, answer_id; var answer, answerId;
answer_id = $(element).attr('id').split("_").slice(1).join("_"); answerId = $(element).attr('id').split('_')
answer = JSON.parse(answers[answer_id]); .slice(1)
.join('_');
answer = JSON.parse(answers[answerId]);
return display.showAnswer(answer); return display.showAnswer(answer);
}, },
choicetextgroup: function (element, display, answers) { choicetextgroup: function(element, display, answers) {
var answer, choice, input_id, _i, _len, _results; var answer, choice, inputId, i, len, results, $element;
element = $(element); $element = $(element);
input_id = element.attr('id').replace(/inputtype_/, ''); inputId = $element.attr('id').replace(/inputtype_/, '');
answer = answers[input_id]; answer = answers[inputId];
_results = []; results = [];
for (_i = 0, _len = answer.length; _i < _len; _i++) { for (i = 0, len = answer.length; i < len; i++) {
choice = answer[_i]; choice = answer[i];
_results.push(element.find("section#forinput" + choice).addClass('choicetextgroup_show_correct')); results.push($element.find('section#forinput' + choice).addClass('choicetextgroup_show_correct'));
} }
return _results; return results;
}, },
imageinput: function (element, display, answers) { imageinput: function(element, display, answers) {
var canvas, container, ctx, id, types; // answers is a dict of (answer_id, answer_text) for each answer for this question.
//
// @Examples:
// {'anwser_id': {
// 'rectangle': '(10,10)-(20,30);(12,12)-(40,60)',
// 'regions': '[[10,10], [30,30], [10, 30], [30, 10]]'
// } }
var canvas, container, id, types, context, $element;
types = { types = {
rectangle: function (ctx, coords) { rectangle: function(ctx, coords) {
var rects, reg; var rects, reg;
reg = /^\(([0-9]+),([0-9]+)\)-\(([0-9]+),([0-9]+)\)$/; reg = /^\(([0-9]+),([0-9]+)\)-\(([0-9]+),([0-9]+)\)$/;
rects = coords.replace(/\s*/g, '').split(/;/); rects = coords.replace(/\s*/g, '').split(/;/);
$.each(rects, function (index, rect) { $.each(rects, function(index, rect) {
var abs, height, points, width; var abs, height, points, width;
abs = Math.abs; abs = Math.abs;
points = reg.exec(rect); points = reg.exec(rect);
if (points) { if (points) {
width = abs(points[3] - points[1]); width = abs(points[3] - points[1]);
height = abs(points[4] - points[2]); height = abs(points[4] - points[2]);
return ctx.rect(points[1], points[2], width, height); ctx.rect(points[1], points[2], width, height);
} }
}); });
ctx.stroke(); ctx.stroke();
return ctx.fill(); return ctx.fill();
}, },
regions: function (ctx, coords) { regions: function(ctx, coords) {
var parseCoords; var parseCoords;
parseCoords = function (coords) { parseCoords = function(coordinates) {
var reg; var reg;
reg = JSON.parse(coords); reg = JSON.parse(coordinates);
if (typeof reg[0][0][0] === "undefined") {
// Regions is list of lists [region1, region2, region3, ...] where regionN
// is disordered list of points: [[1,1], [100,100], [50,50], [20, 70]].
// If there is only one region in the list, simpler notation can be used:
// regions="[[10,10], [30,30], [10, 30], [30, 10]]" (without explicitly
// setting outer list)
if (typeof reg[0][0][0] === 'undefined') {
// we have [[1,2],[3,4],[5,6]] - single region
// instead of [[[1,2],[3,4],[5,6], [[1,2],[3,4],[5,6]]]
// or [[[1,2],[3,4],[5,6]]] - multiple regions syntax
reg = [reg]; reg = [reg];
} }
return reg; return reg;
}; };
return $.each(parseCoords(coords), function (index, region) { return $.each(parseCoords(coords), function(index, region) {
ctx.beginPath(); ctx.beginPath();
$.each(region, function (index, point) { $.each(region, function(idx, point) {
if (index === 0) { if (idx === 0) {
return ctx.moveTo(point[0], point[1]); return ctx.moveTo(point[0], point[1]);
} else { } else {
return ctx.lineTo(point[0], point[1]); return ctx.lineTo(point[0], point[1]);
...@@ -1002,70 +1157,98 @@ ...@@ -1002,70 +1157,98 @@
}); });
} }
}; };
element = $(element); $element = $(element);
id = element.attr('id').replace(/inputtype_/, ''); id = $element.attr('id').replace(/inputtype_/, '');
container = element.find("#answer_" + id); container = $element.find('#answer_' + id);
canvas = document.createElement('canvas'); canvas = document.createElement('canvas');
canvas.width = container.data('width'); canvas.width = container.data('width');
canvas.height = container.data('height'); canvas.height = container.data('height');
if (canvas.getContext) { if (canvas.getContext) {
ctx = canvas.getContext('2d'); context = canvas.getContext('2d');
} else { } else {
return console.log('Canvas is not supported.'); console.log('Canvas is not supported.'); // eslint-disable-line no-console
} }
ctx.fillStyle = 'rgba(255,255,255,.3)'; context.fillStyle = 'rgba(255,255,255,.3)';
ctx.strokeStyle = "#FF0000"; context.strokeStyle = '#FF0000';
ctx.lineWidth = "2"; context.lineWidth = '2';
if (answers[id]) { if (answers[id]) {
$.each(answers[id], function (key, value) { $.each(answers[id], function(key, value) {
if ((types[key] != null) && value) { if ((types[key] !== null && types[key] !== undefined) && value) {
return types[key](ctx, value); types[key](context, value);
} }
}); });
return container.html(canvas); container.html(canvas);
} else { } else {
return console.log("Answer is absent for image input with id=" + id); console.log('Answer is absent for image input with id=' + id); // eslint-disable-line no-console
} }
} }
}; };
Problem.prototype.inputtypeHideAnswerMethods = { Problem.prototype.inputtypeHideAnswerMethods = {
choicegroup: function (element, display) { choicegroup: function(element) {
element = $(element); var $element = $(element);
return element.find('label').removeClass('choicegroup_correct'); return $element.find('label').removeClass('choicegroup_correct');
}, },
javascriptinput: function (element, display) { javascriptinput: function(element, display) {
return display.hideAnswer(); return display.hideAnswer();
}, },
choicetextgroup: function (element, display) { choicetextgroup: function(element) {
element = $(element); var $element = $(element);
return element.find("section[id^='forinput']").removeClass('choicetextgroup_show_correct'); return $element.find('section[id^="forinput"]').removeClass('choicetextgroup_show_correct');
} }
}; };
Problem.prototype.disableAllButtonsWhileRunning = function (operationCallback, isFromCheckOperation) { /**
var _this = this; * Used to keep the buttons disabled while operationCallback is running.
*
* params:
* 'operationCallback' is an operation to be run.
* isFromCheckOperation' is a boolean to keep track if 'operationCallback' was
* from submit, if so then text of submit button will be changed as well.
*
*/
Problem.prototype.disableAllButtonsWhileRunning = function(operationCallback, isFromCheckOperation) {
var that = this;
this.enableAllButtons(false, isFromCheckOperation); this.enableAllButtons(false, isFromCheckOperation);
return operationCallback().always(function () { return operationCallback().always(function() {
return _this.enableAllButtons(true, isFromCheckOperation); return that.enableAllButtons(true, isFromCheckOperation);
}); });
}; };
Problem.prototype.enableAllButtons = function (enable, isFromCheckOperation) { /**
* Used to enable/disable all buttons in problem.
*
* params:
* 'enable' is a boolean to determine enabling/disabling of buttons.
* 'isFromCheckOperation' is a boolean to keep track if operation was initiated
* from submit so that text of submit button will also be changed while disabling/enabling
* the submit button.
*/
Problem.prototype.enableAllButtons = function(enable, isFromCheckOperation) {
// Called by disableAllButtonsWhileRunning to automatically disable all buttons while check,reset, or
// save internal are running. Then enable all the buttons again after it is done.
if (enable) { if (enable) {
this.resetButton.add(this.saveButton).add(this.hintButton).add(this.showButton).removeAttr('disabled'); this.resetButton.add(this.saveButton).add(this.hintButton).add(this.showButton).
removeAttr('disabled');
} else { } else {
this.resetButton.add(this.saveButton).add(this.hintButton).add(this.showButton).attr({ this.resetButton.add(this.saveButton).add(this.hintButton).add(this.showButton).
'disabled': 'disabled' attr({disabled: 'disabled'});
});
} }
return this.enableSubmitButton(enable, isFromCheckOperation); return this.enableSubmitButton(enable, isFromCheckOperation);
}; };
Problem.prototype.enableSubmitButton = function (enable, changeText) { /**
* Used to disable submit button to reduce chance of accidental double-submissions.
*
* params:
* 'enable' is a boolean to determine enabling/disabling of submit button.
* 'changeText' is a boolean to determine if there is need to change the
* text of submit button as well.
*/
Problem.prototype.enableSubmitButton = function(enable, changeText) {
var submitCanBeEnabled; var submitCanBeEnabled;
if (changeText == null) { if (changeText === null || changeText === undefined) {
changeText = true; changeText = true; // eslint-disable-line no-param-reassign
} }
if (enable) { if (enable) {
submitCanBeEnabled = this.submitButton.data('should-enable-submit-button') === 'True'; submitCanBeEnabled = this.submitButton.data('should-enable-submit-button') === 'True';
...@@ -1073,78 +1256,75 @@ ...@@ -1073,78 +1256,75 @@
this.submitButton.removeAttr('disabled'); this.submitButton.removeAttr('disabled');
} }
if (changeText) { if (changeText) {
return this.submitButtonLabel.text(this.submitButtonSubmitText); this.submitButtonLabel.text(this.submitButtonSubmitText);
} }
} else { } else {
this.submitButton.attr({ this.submitButton.attr({disabled: 'disabled'});
'disabled': 'disabled'
});
if (changeText) { if (changeText) {
return this.submitButtonLabel.text(this.submitButtonSubmittingText); this.submitButtonLabel.text(this.submitButtonSubmittingText);
} }
} }
}; };
Problem.prototype.enableSubmitButtonAfterResponse = function () { Problem.prototype.enableSubmitButtonAfterResponse = function() {
this.has_response = true; this.has_response = true;
if (!this.has_timed_out) { if (!this.has_timed_out) {
// Server has returned response before our timeout.
return this.enableSubmitButton(false); return this.enableSubmitButton(false);
} else { } else {
return this.enableSubmitButton(true); return this.enableSubmitButton(true);
} }
}; };
Problem.prototype.enableSubmitButtonAfterTimeout = function () { Problem.prototype.enableSubmitButtonAfterTimeout = function() {
var enableSubmitButton, var enableSubmitButton,
_this = this; that = this;
this.has_timed_out = false; this.has_timed_out = false;
this.has_response = false; this.has_response = false;
enableSubmitButton = function () { enableSubmitButton = function() {
_this.has_timed_out = true; that.has_timed_out = true;
if (_this.has_response) { if (that.has_response) {
return _this.enableSubmitButton(true); that.enableSubmitButton(true);
} }
}; };
return window.setTimeout(enableSubmitButton, 750); return window.setTimeout(enableSubmitButton, 750);
}; };
Problem.prototype.hint_button = function () { Problem.prototype.hint_button = function() {
var hint_container, hint_index, next_index, // Store the index of the currently shown hint as an attribute.
_this = this; // Use that to compute the next hint number when the button is clicked.
hint_container = this.$('.problem-hint'); var hintContainer, hintIndex, nextIndex,
hint_index = hint_container.attr('hint_index'); that = this;
if (hint_index === void 0) { hintContainer = this.$('.problem-hint');
next_index = 0; hintIndex = hintContainer.attr('hint_index');
if (hintIndex === void 0) {
nextIndex = 0;
} else { } else {
next_index = parseInt(hint_index) + 1; nextIndex = parseInt(hintIndex, 10) + 1;
} }
return $.postWithPrefix("" + this.url + "/hint_button", { return $.postWithPrefix('' + this.url + '/hint_button', {
hint_index: next_index, hint_index: nextIndex,
input_id: this.id input_id: this.id
}, function (response) { }, function(response) {
var hint_msg_container; var hintMsgContainer;
if (response.success) { if (response.success) {
hint_msg_container = _this.$('.problem-hint .notification-message'); hintMsgContainer = that.$('.problem-hint .notification-message');
hint_container.attr('hint_index', response.hint_index); hintContainer.attr('hint_index', response.hint_index);
edx.HtmlUtils.setHtml(hint_msg_container, edx.HtmlUtils.HTML(response.msg)); edx.HtmlUtils.setHtml(hintMsgContainer, edx.HtmlUtils.HTML(response.msg));
MathJax.Hub.Queue(['Typeset', MathJax.Hub, hint_container[0]]); MathJax.Hub.Queue(['Typeset', MathJax.Hub, hintContainer[0]]);
if (response.should_enable_next_hint) { if (response.should_enable_next_hint) {
_this.hintButton.removeAttr('disabled'); that.hintButton.removeAttr('disabled');
} else { } else {
_this.hintButton.attr({ that.hintButton.attr({disabled: 'disabled'});
'disabled': 'disabled'
});
} }
_this.el.find('.notification-hint').show(); that.el.find('.notification-hint').show();
return _this.focus_on_hint_notification(); that.focus_on_hint_notification();
} else { } else {
return _this.gentle_alert(response.msg); that.gentle_alert(response.msg);
} }
}); });
}; };
return Problem; return Problem;
}).call(this); }).call(this);
}).call(this); }).call(this);
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