Commit ab8b5776 by Peter Baratta

Moved js file and added improvments

- converted underscores to camelcase
- rearranged code in JS file to actually make sense
- Jasmine tests!
parent 261b5c45
......@@ -1096,7 +1096,7 @@ class FormulaEquationInput(InputTypeBase):
reported_status = self.status
return {
'previewer': '/static/js/capa/formula_equation_preview.js',
'previewer': '/static/js/capa/src/formula_equation_preview.js',
'reported_status': reported_status
}
......
......@@ -800,12 +800,14 @@ class FormulaEquationTest(unittest.TestCase):
"""
context = self.the_input._get_render_context() # pylint: disable=W0212
expected = {'id': 'prob_1_2',
expected = {
'id': 'prob_1_2',
'value': 'x^2+1/2',
'status': 'unanswered',
'reported_status': '',
'msg': '',
'size': self.size,
'previewer': '/static/js/capa/formula_equation_preview.js',
'previewer': '/static/js/capa/src/formula_equation_preview.js',
}
self.assertEqual(context, expected)
......
(function () {
var min_delay = 300; // milliseconds between AJAX requests
// Dictionary holding information indexed by the IDs of the problems
var preview_data = {};
function update() {
/**
Given a user input, either send a request or enqueue one to be sent.
Don't call `send_request` if it's been less than `min_delay` ms.
Also, indicate that it is loading (using the loading icon).
*/
var data = preview_data[this.id];
var time_since_last = Date.now() - data.last_sent;
var bound_send_request = send_request.bind(this);
if (time_since_last >= min_delay) {
// If it's been long enough, just send the request
bound_send_request();
}
else {
// Otherwise, enqueue.
if (data.timeout_id !== null) {
// Clear any other queued requests.
window.clearTimeout(data.timeout_id);
}
// Wait for the rest of the `min_delay`.
// Store `timeout_id`
var wait_time = min_delay - time_since_last;
data.timeout_id = window.setTimeout(bound_send_request, wait_time);
}
// Show the loading icon.
data.$loading.css('visibility', 'visible');
}
function send_request() {
/**
Fire off a request for a preview of the current value.
Also send along the time it was sent, and store that locally.
*/
var data = preview_data[this.id];
data.timeout_id = null;
var $this = $(this); // cache the jQuery object
// Save the time.
var now = Date.now();
data.last_sent = now;
// Find the closest parent problems-wrapper and use that url.
var url = $this.closest('.problems-wrapper').data('url');
// Grab the input id from the input.
var input_id = $this.data('input-id')
Problem.inputAjax(url, input_id, 'preview_formcalc', {
"formula" : this.value,
"request_start" : now
}, create_handler(data));
// TODO what happens when an AJAX call is lost?
}
function create_handler(data) {
/** Create a closure for `data` */
return (function (response) {
/**
Respond to the preview request
Optionally, stop if it is outdated (a later request arrived
back earlier)
Otherwise:
-Refresh the MathJax
-Stop the loading icon if need be
-Save which request this is
*/
if (response.request_start == data.last_sent &&
data.timeout_id === null) {
data.$loading.css('visibility', 'hidden'); // Disable icon
}
if (response.request_start <= data.request_visible) {
return; // This is an old request.
}
// Save the value of the last response displayed.
data.request_visible = response.request_start;
var jax = MathJax.Hub.getAllJax(data.$preview[0])[0];
var math_code;
if (response.error) {
// TODO: wait for a bit to display error
math_code = "\text{" + response.error + "}";
} else {
math_code = response.preview;
}
// Set the text as the latex code, and then update the MathJax.
MathJax.Hub.Queue(
['Text', jax, math_code],
['Reprocess', jax]
);
});
}
inputs = $('.formulaequationinput input');
// Store information for each input and cache the jQuery objects
inputs.each(function () {
var prev_id = "#" + this.id + "_preview";
preview_data[this.id] = {
$preview: $(prev_id),
$loading: $(prev_id + " img.loading"),
last_sent: 0, // The time of the one that was last sent
request_visible: 0, // The original time of the visible request
timeout_id: null // If there is a timeout, store its ID here
};
});
// update on load
inputs.each(update);
// and on every change
inputs.bind("input", update);
}).call(this);
var formulaEquationPreview = {
minDelay: 300, // Minimum time between requests sent out.
errorDelay: 1500 // Wait time before showing error (prevent frustration).
};
/** Setup the FormulaEquationInputs and associated javascript code. */
formulaEquationPreview.enable = function () {
/**
* Accumulate all the variables and attach event handlers.
* This includes rate-limiting `sendRequest` and creating a closure for
* its callback.
*/
function setupInput() {
var inputData = {
// These are the mutable values
lastSent: 0,
isWaitingForRequest: false,
requestVisible: 0,
errorDelayTimeout: null
};
// Other elements of `inputData` serve to hold pointers to variables.
var $this = $(this); // cache the jQuery object
// Find the closest parent problems-wrapper and use that url for Ajax.
inputData.url = $this.closest('.problems-wrapper').data('url');
// Grab the input id from the input.
inputData.inputId = $this.data('input-id')
// Store the DOM/MathJax elements in which visible output occurs.
inputData.$preview = $("#" + this.id + "_preview");
// Note: sometimes MathJax hasn't finished loading yet.
inputData.jax = MathJax.Hub.getAllJax(inputData.$preview[0])[0];
inputData.$img = inputData.$preview.find("img.loading");
// Give the callback access to `inputData` (fill in first parameter).
inputData.requestCallback = _.partial(updatePage, inputData);
// Limit `sendRequest` and have it show the loading icon.
var throttledRequest = _.throttle(
sendRequest,
formulaEquationPreview.minDelay,
{leading: false}
);
// The following acts as a closure of `inputData`.
var initializeRequest = function () {
// Show the loading icon.
inputData.$img.css('visibility', 'visible');
inputData.isWaitingForRequest = true;
throttledRequest(inputData, this.value);
};
$this.bind("input", initializeRequest);
// send an initial
initializeRequest.call(this);
}
/**
* Fire off a request for a preview of the current value.
* Also send along the time it was sent, and store that locally.
*/
function sendRequest(inputData, formula) {
// Save the time.
var now = Date.now();
inputData.lastSent = now;
// We're sending it.
inputData.isWaitingForRequest = false;
if (formula) {
// Send the request.
Problem.inputAjax(
inputData.url,
inputData.inputId,
'preview_formcalc',
{"formula" : formula, "request_start" : now},
inputData.requestCallback
);
// ).fail(function () {
// // This is run when ajax call fails.
// // Have an error message and other stuff here?
// inputData.$img.css('visibility', 'hidden');
// }); */
}
else {
inputData.requestCallback({
preview: '',
request_start: now
});
}
}
/**
* Respond to the preview request if need be.
* Stop if it is outdated (i.e. a later request arrived back earlier)
* Otherwise:
* -Refresh the MathJax
* -Stop the loading icon if this is the most recent request
* -Save which request is visible
*/
function updatePage(inputData, response) {
var requestStart = response['request_start'];
if (requestStart == inputData.lastSent &&
!inputData.isWaitingForRequest) {
// Disable icon.
inputData.$img.css('visibility', 'hidden');
}
if (requestStart <= inputData.requestVisible) {
// This is an old request.
return;
}
// Save the value of the last response displayed.
inputData.requestVisible = requestStart;
// Prevent an old error message from showing.
if (inputData.errorWaitTimeout != null) {
window.clearTimeout(inputData.errorWaitTimeout);
}
function display(latex) {
// Load jax if it failed before.
if (!inputData.jax) {
inputData.jax = MathJax.Hub.getAllJax(inputData.$preview[0])[0];
}
// Set the text as the latex code, and then update the MathJax.
MathJax.Hub.Queue(
['Text', inputData.jax, latex],
['Reprocess', inputData.jax]
);
}
if (response.error) {
inputData.errorWaitTimeout = window.setTimeout(function () {
display("\\text{" + response.error + "}");
}, formulaEquationPreview.errorDelay);
} else {
display(response.preview);
}
}
// Invoke the setup method.
$('.formulaequationinput input').each(setupInput);
};
formulaEquationPreview.enable();
......@@ -121,6 +121,7 @@ end
static_js_dirs = Dir["common/lib/*"].select{|lib| File.directory?(lib)}
static_js_dirs << 'common/static/coffee'
static_js_dirs << 'common/static/js'
static_js_dirs.select!{|lib| !Dir["#{lib}/**/spec"].empty?}
static_js_dirs.each do |dir|
......
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