Commit 735e3b01 by Peter Baratta

Create a new response type for Numerical/Formula

Named `FormulaEquationInput` (name up for debate)

- Based off ChemEqnIn
- Add FormulaEquationInput in inputtypes.py
- Add a call to a skeleton method for a preview

javascript:

- Queue up some MathJax
- Put some ordering on the AJAX requests: add a parameter when the request was started, when it returns check that it isn't outdated before displaying the preview
- Tests

Note: we moved the `jsinput` tests and DISABLED them, because they were causing the tests to fail.
parent a1162cbb
...@@ -16,6 +16,8 @@ Module containing the problem elements which render into input objects ...@@ -16,6 +16,8 @@ Module containing the problem elements which render into input objects
- crystallography - crystallography
- vsepr_input - vsepr_input
- drag_and_drop - drag_and_drop
- formulaequationinput
- chemicalequationinput
These are matched by *.html files templates/*.html which are mako templates with the These are matched by *.html files templates/*.html which are mako templates with the
actual html. actual html.
...@@ -47,6 +49,7 @@ import pyparsing ...@@ -47,6 +49,7 @@ import pyparsing
from .registry import TagRegistry from .registry import TagRegistry
from chem import chemcalc from chem import chemcalc
from preview import latex_preview
import xqueue_interface import xqueue_interface
from datetime import datetime from datetime import datetime
...@@ -531,7 +534,7 @@ class TextLine(InputTypeBase): ...@@ -531,7 +534,7 @@ class TextLine(InputTypeBase):
is used e.g. for embedding simulations turned into questions. is used e.g. for embedding simulations turned into questions.
Example: Example:
<texline math="1" trailing_text="m/s" /> <textline math="1" trailing_text="m/s" />
This example will render out a text line with a math preview and the text 'm/s' This example will render out a text line with a math preview and the text 'm/s'
after the end of the text line. after the end of the text line.
...@@ -1037,15 +1040,16 @@ class ChemicalEquationInput(InputTypeBase): ...@@ -1037,15 +1040,16 @@ class ChemicalEquationInput(InputTypeBase):
result = {'preview': '', result = {'preview': '',
'error': ''} 'error': ''}
formula = data['formula'] try:
if formula is None: formula = data['formula']
except KeyError:
result['error'] = "No formula specified." result['error'] = "No formula specified."
return result return result
try: try:
result['preview'] = chemcalc.render_to_html(formula) result['preview'] = chemcalc.render_to_html(formula)
except pyparsing.ParseException as p: except pyparsing.ParseException as p:
result['error'] = "Couldn't parse formula: {0}".format(p) result['error'] = u"Couldn't parse formula: {0}".format(p.msg)
except Exception: except Exception:
# this is unexpected, so log # this is unexpected, so log
log.warning( log.warning(
...@@ -1056,6 +1060,98 @@ class ChemicalEquationInput(InputTypeBase): ...@@ -1056,6 +1060,98 @@ class ChemicalEquationInput(InputTypeBase):
registry.register(ChemicalEquationInput) registry.register(ChemicalEquationInput)
#-------------------------------------------------------------------------
class FormulaEquationInput(InputTypeBase):
"""
An input type for entering formula equations. Supports live preview.
Example:
<formulaequationinput size="50"/>
options: size -- width of the textbox.
"""
template = "formulaequationinput.html"
tags = ['formulaequationinput']
@classmethod
def get_attributes(cls):
"""
Can set size of text field.
"""
return [Attribute('size', '20'), ]
def _extra_context(self):
"""
TODO (vshnayder): Get rid of 'previewer' once we have a standard way of requiring js to be loaded.
"""
# `reported_status` is basically `status`, except we say 'unanswered'
reported_status = ''
if self.status == 'unsubmitted':
reported_status = 'unanswered'
elif self.status in ('correct', 'incorrect', 'incomplete'):
reported_status = self.status
return {
'previewer': '/static/js/capa/src/formula_equation_preview.js',
'reported_status': reported_status
}
def handle_ajax(self, dispatch, get):
'''
Since we only have formcalc preview this input, check to see if it
matches the corresponding dispatch and send it through if it does
'''
if dispatch == 'preview_formcalc':
return self.preview_formcalc(get)
return {}
def preview_formcalc(self, get):
"""
Render an preview of a formula or equation. `get` should
contain a key 'formula' with a math expression.
Returns a json dictionary:
{
'preview' : '<some latex>' or ''
'error' : 'the-error' or ''
'request_start' : <time sent with request>
}
"""
result = {'preview': '',
'error': ''}
try:
formula = get['formula']
except KeyError:
result['error'] = "No formula specified."
return result
result['request_start'] = int(get.get('request_start', 0))
try:
# TODO add references to valid variables and functions
# At some point, we might want to mark invalid variables as red
# or something, and this is where we would need to pass those in.
result['preview'] = latex_preview(formula)
except pyparsing.ParseException as err:
result['error'] = "Sorry, couldn't parse formula"
result['formula'] = formula
except Exception:
# this is unexpected, so log
log.warning(
"Error while previewing formula", exc_info=True
)
result['error'] = "Error while rendering preview"
return result
registry.register(FormulaEquationInput)
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
......
...@@ -822,7 +822,7 @@ class NumericalResponse(LoncapaResponse): ...@@ -822,7 +822,7 @@ class NumericalResponse(LoncapaResponse):
response_tag = 'numericalresponse' response_tag = 'numericalresponse'
hint_tag = 'numericalhint' hint_tag = 'numericalhint'
allowed_inputfields = ['textline'] allowed_inputfields = ['textline', 'formulaequationinput']
required_attributes = ['answer'] required_attributes = ['answer']
max_inputfields = 1 max_inputfields = 1
...@@ -837,11 +837,6 @@ class NumericalResponse(LoncapaResponse): ...@@ -837,11 +837,6 @@ class NumericalResponse(LoncapaResponse):
self.tolerance = contextualize_text(self.tolerance_xml, context) self.tolerance = contextualize_text(self.tolerance_xml, context)
except IndexError: # xpath found an empty list, so (...)[0] is the error except IndexError: # xpath found an empty list, so (...)[0] is the error
self.tolerance = '0' self.tolerance = '0'
try:
self.answer_id = xml.xpath('//*[@id=$id]//textline/@id',
id=xml.get('id'))[0]
except IndexError: # Same as above
self.answer_id = None
def get_score(self, student_answers): def get_score(self, student_answers):
'''Grade a numeric response ''' '''Grade a numeric response '''
...@@ -936,7 +931,7 @@ class CustomResponse(LoncapaResponse): ...@@ -936,7 +931,7 @@ class CustomResponse(LoncapaResponse):
'chemicalequationinput', 'vsepr_input', 'chemicalequationinput', 'vsepr_input',
'drag_and_drop_input', 'editamoleculeinput', 'drag_and_drop_input', 'editamoleculeinput',
'designprotein2dinput', 'editageneinput', 'designprotein2dinput', 'editageneinput',
'annotationinput', 'jsinput'] 'annotationinput', 'jsinput', 'formulaequationinput']
def setup_response(self): def setup_response(self):
xml = self.xml xml = self.xml
...@@ -1692,7 +1687,7 @@ class FormulaResponse(LoncapaResponse): ...@@ -1692,7 +1687,7 @@ class FormulaResponse(LoncapaResponse):
response_tag = 'formularesponse' response_tag = 'formularesponse'
hint_tag = 'formulahint' hint_tag = 'formulahint'
allowed_inputfields = ['textline'] allowed_inputfields = ['textline', 'formulaequationinput']
required_attributes = ['answer', 'samples'] required_attributes = ['answer', 'samples']
max_inputfields = 1 max_inputfields = 1
...@@ -1737,7 +1732,7 @@ class FormulaResponse(LoncapaResponse): ...@@ -1737,7 +1732,7 @@ class FormulaResponse(LoncapaResponse):
samples.split('@')[1].split('#')[0].split(':'))) samples.split('@')[1].split('#')[0].split(':')))
ranges = dict(zip(variables, sranges)) ranges = dict(zip(variables, sranges))
for i in range(numsamples): for _ in range(numsamples):
instructor_variables = self.strip_dict(dict(self.context)) instructor_variables = self.strip_dict(dict(self.context))
student_variables = dict() student_variables = dict()
# ranges give numerical ranges for testing # ranges give numerical ranges for testing
...@@ -1767,27 +1762,39 @@ class FormulaResponse(LoncapaResponse): ...@@ -1767,27 +1762,39 @@ class FormulaResponse(LoncapaResponse):
) )
except UndefinedVariable as uv: except UndefinedVariable as uv:
log.debug( log.debug(
'formularesponse: undefined variable in given=%s' % given) 'formularesponse: undefined variable in given=%s',
given
)
raise StudentInputError( raise StudentInputError(
"Invalid input: " + uv.message + " not permitted in answer") "Invalid input: " + uv.message + " not permitted in answer"
)
except ValueError as ve: except ValueError as ve:
if 'factorial' in ve.message: if 'factorial' in ve.message:
# This is thrown when fact() or factorial() is used in a formularesponse answer # This is thrown when fact() or factorial() is used in a formularesponse answer
# that tests on negative and/or non-integer inputs # that tests on negative and/or non-integer inputs
# ve.message will be: `factorial() only accepts integral values` or `factorial() not defined for negative values` # ve.message will be: `factorial() only accepts integral values` or
# `factorial() not defined for negative values`
log.debug( log.debug(
'formularesponse: factorial function used in response that tests negative and/or non-integer inputs. given={0}'.format(given)) ('formularesponse: factorial function used in response '
'that tests negative and/or non-integer inputs. '
'given={0}').format(given)
)
raise StudentInputError( raise StudentInputError(
"factorial function not permitted in answer for this problem. Provided answer was: {0}".format(given)) ("factorial function not permitted in answer "
"for this problem. Provided answer was: "
"{0}").format(cgi.escape(given))
)
# If non-factorial related ValueError thrown, handle it the same as any other Exception # If non-factorial related ValueError thrown, handle it the same as any other Exception
log.debug('formularesponse: error {0} in formula'.format(ve)) log.debug('formularesponse: error {0} in formula'.format(ve))
raise StudentInputError("Invalid input: Could not parse '%s' as a formula" % raise StudentInputError("Invalid input: Could not parse '%s' as a formula" %
cgi.escape(given)) cgi.escape(given))
except Exception as err: except Exception as err:
# traceback.print_exc() # traceback.print_exc()
log.debug('formularesponse: error %s in formula' % err) log.debug('formularesponse: error %s in formula', err)
raise StudentInputError("Invalid input: Could not parse '%s' as a formula" % raise StudentInputError("Invalid input: Could not parse '%s' as a formula" %
cgi.escape(given)) cgi.escape(given))
# No errors in student's response--actually test for correctness
if not compare_with_tolerance(student_result, instructor_result, self.tolerance): if not compare_with_tolerance(student_result, instructor_result, self.tolerance):
return "incorrect" return "incorrect"
return "correct" return "correct"
......
<section id="formulaequationinput_${id}" class="formulaequationinput">
<div class="${reported_status}" id="status_${id}">
<input type="text" name="input_${id}" id="input_${id}"
data-input-id="${id}" value="${value|h}"
% if size:
size="${size}"
% endif
/>
<p class="status">${reported_status}</p>
<div id="input_${id}_preview" class="equation">
\[\]
<img src="/static/images/spinner.gif" class="loading"/>
</div>
<p id="answer_${id}" class="answer"></p>
</div>
<div class="script_placeholder" data-src="${previewer}"/>
</section>
...@@ -448,6 +448,32 @@ class TextlineTemplateTest(TemplateTestCase): ...@@ -448,6 +448,32 @@ class TextlineTemplateTest(TemplateTestCase):
self.assert_has_text(xml, xpath, self.context['msg']) self.assert_has_text(xml, xpath, self.context['msg'])
class FormulaEquationInputTemplateTest(TemplateTestCase):
"""
Test make template for `<formulaequationinput>`s.
"""
TEMPLATE_NAME = 'formulaequationinput.html'
def setUp(self):
self.context = {
'id': 2,
'value': 'PREFILLED_VALUE',
'status': 'unsubmitted',
'previewer': 'file.js',
'reported_status': 'REPORTED_STATUS',
}
super(FormulaEquationInputTemplateTest, self).setUp()
def test_no_size(self):
xml = self.render_to_xml(self.context)
self.assert_no_xpath(xml, "//input[@size]", self.context)
def test_size(self):
self.context['size'] = '40'
xml = self.render_to_xml(self.context)
self.assert_has_xpath(xml, "//input[@size='40']", self.context)
class AnnotationInputTemplateTest(TemplateTestCase): class AnnotationInputTemplateTest(TemplateTestCase):
""" """
Test mako template for `<annotationinput>` input. Test mako template for `<annotationinput>` input.
......
...@@ -32,7 +32,7 @@ $wrongans</tt> to see a hint.</p> ...@@ -32,7 +32,7 @@ $wrongans</tt> to see a hint.</p>
<formularesponse samples="x@-5:5#11" id="11" answer="$answer"> <formularesponse samples="x@-5:5#11" id="11" answer="$answer">
<responseparam description="Numerical Tolerance" type="tolerance" default="0.001" name="tol" /> <responseparam description="Numerical Tolerance" type="tolerance" default="0.001" name="tol" />
<text>y = <textline size="25" /></text> <text>y = <formulaequationinput size="25" /></text>
<hintgroup> <hintgroup>
<formulahint samples="x@-5:5#11" answer="$wrongans" name="inversegrad"> <formulahint samples="x@-5:5#11" answer="$wrongans" name="inversegrad">
</formulahint> </formulahint>
......
...@@ -173,7 +173,7 @@ section.problem { ...@@ -173,7 +173,7 @@ section.problem {
} }
} }
&.incorrect, &.ui-icon-close { &.incorrect, &.incomplete, &.ui-icon-close {
p.status { p.status {
@include inline-block(); @include inline-block();
background: url('../images/incorrect-icon.png') center center no-repeat; background: url('../images/incorrect-icon.png') center center no-repeat;
...@@ -214,6 +214,16 @@ section.problem { ...@@ -214,6 +214,16 @@ section.problem {
clear: both; clear: both;
margin-top: 3px; margin-top: 3px;
.MathJax_Display {
display: inline-block;
width: auto;
}
img.loading {
display: inline-block;
padding-left: 10px;
}
span { span {
margin-bottom: 0; margin-bottom: 0;
...@@ -265,7 +275,7 @@ section.problem { ...@@ -265,7 +275,7 @@ section.problem {
width: 25px; width: 25px;
} }
&.incorrect, &.ui-icon-close { &.incorrect, &.incomplete, &.ui-icon-close {
@include inline-block(); @include inline-block();
background: url('../images/incorrect-icon.png') center center no-repeat; background: url('../images/incorrect-icon.png') center center no-repeat;
height: 20px; height: 20px;
......
...@@ -121,18 +121,18 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -121,18 +121,18 @@ describe 'MarkdownEditingDescriptor', ->
<p>Enter the numerical value of Pi:</p> <p>Enter the numerical value of Pi:</p>
<numericalresponse answer="3.14159"> <numericalresponse answer="3.14159">
<responseparam type="tolerance" default=".02" /> <responseparam type="tolerance" default=".02" />
<textline /> <formulaequationinput />
</numericalresponse> </numericalresponse>
<p>Enter the approximate value of 502*9:</p> <p>Enter the approximate value of 502*9:</p>
<numericalresponse answer="4518"> <numericalresponse answer="4518">
<responseparam type="tolerance" default="15%" /> <responseparam type="tolerance" default="15%" />
<textline /> <formulaequationinput />
</numericalresponse> </numericalresponse>
<p>Enter the number of fingers on a human hand:</p> <p>Enter the number of fingers on a human hand:</p>
<numericalresponse answer="5"> <numericalresponse answer="5">
<textline /> <formulaequationinput />
</numericalresponse> </numericalresponse>
<solution> <solution>
...@@ -157,7 +157,7 @@ describe 'MarkdownEditingDescriptor', -> ...@@ -157,7 +157,7 @@ describe 'MarkdownEditingDescriptor', ->
<p>Enter 0 with a tolerance:</p> <p>Enter 0 with a tolerance:</p>
<numericalresponse answer="0"> <numericalresponse answer="0">
<responseparam type="tolerance" default=".02" /> <responseparam type="tolerance" default=".02" />
<textline /> <formulaequationinput />
</numericalresponse> </numericalresponse>
......
...@@ -239,7 +239,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor ...@@ -239,7 +239,7 @@ class @MarkdownEditingDescriptor extends XModule.Descriptor
} else { } else {
string = '<numericalresponse answer="' + floatValue + '">\n'; string = '<numericalresponse answer="' + floatValue + '">\n';
} }
string += ' <textline />\n'; string += ' <formulaequationinput />\n';
string += '</numericalresponse>\n\n'; string += '</numericalresponse>\n\n';
} else { } else {
string = '<stringresponse answer="' + p + '" type="ci">\n <textline size="20"/>\n</stringresponse>\n\n'; string = '<stringresponse answer="' + p + '" type="ci">\n <textline size="20"/>\n</stringresponse>\n\n';
......
...@@ -24,15 +24,15 @@ data: | ...@@ -24,15 +24,15 @@ data: |
</script> </script>
<p>Give an equation for the relativistic energy of an object with mass m. Explicitly indicate multiplication with a <tt>*</tt> symbol.</p> <p>Give an equation for the relativistic energy of an object with mass m. Explicitly indicate multiplication with a <tt>*</tt> symbol.</p>
<formularesponse type="cs" samples="m,c@1,2:3,4#10" answer="m*c^2"> <formularesponse type="cs" samples="m,c@1,2:3,4#10" answer="m*c^2">
<responseparam type="tolerance" default="0.00001"/> <responseparam type="tolerance" default="0.00001"/>
<br/><text>E =</text> <textline size="40" math="1" /> <br/><text>E =</text> <formulaequationinput size="40" />
</formularesponse> </formularesponse>
<p>The answer to this question is (R_1*R_2)/R_3. </p> <p>The answer to this question is (R_1*R_2)/R_3. </p>
<formularesponse type="ci" samples="R_1,R_2,R_3@1,2,3:3,4,5#10" answer="$VoVi"> <formularesponse type="ci" samples="R_1,R_2,R_3@1,2,3:3,4,5#10" answer="$VoVi">
<responseparam type="tolerance" default="0.00001"/> <responseparam type="tolerance" default="0.00001"/>
<textline size="40" math="1" /> <formulaequationinput size="40" />
</formularesponse> </formularesponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
......
...@@ -119,9 +119,8 @@ data: | ...@@ -119,9 +119,8 @@ data: |
<p> <p>
<p style="display:inline">Energy saved = </p> <p style="display:inline">Energy saved = </p>
<numericalresponse inline="1" answer="0.52"> <numericalresponse inline="1" answer="0.52">
<textline inline="1"> <responseparam description="Numerical Tolerance" type="tolerance" default="0.02" name="tol"/>
<responseparam description="Numerical Tolerance" type="tolerance" default="0.02" name="tol"/> <formulaequationinput/>
</textline>
</numericalresponse> </numericalresponse>
<p style="display:inline">&#xA0;EJ/year</p> <p style="display:inline">&#xA0;EJ/year</p>
</p> </p>
......
...@@ -47,19 +47,19 @@ data: | ...@@ -47,19 +47,19 @@ data: |
<p>Enter the numerical value of Pi: <p>Enter the numerical value of Pi:
<numericalresponse answer="3.14159"> <numericalresponse answer="3.14159">
<responseparam type="tolerance" default=".02" /> <responseparam type="tolerance" default=".02" />
<textline /> <formulaequationinput />
</numericalresponse> </numericalresponse>
</p> </p>
<p>Enter the approximate value of 502*9: <p>Enter the approximate value of 502*9:
<numericalresponse answer="$computed_response"> <numericalresponse answer="$computed_response">
<responseparam type="tolerance" default="15%"/> <responseparam type="tolerance" default="15%"/>
<textline /> <formulaequationinput />
</numericalresponse> </numericalresponse>
</p> </p>
<p>Enter the number of fingers on a human hand: <p>Enter the number of fingers on a human hand:
<numericalresponse answer="5"> <numericalresponse answer="5">
<textline /> <formulaequationinput />
</numericalresponse> </numericalresponse>
</p> </p>
<solution> <solution>
......
...@@ -28,7 +28,7 @@ class CHModuleFactory(object): ...@@ -28,7 +28,7 @@ class CHModuleFactory(object):
<p>The answer is correct if it is within a specified numerical tolerance of the expected answer.</p> <p>The answer is correct if it is within a specified numerical tolerance of the expected answer.</p>
<p>Enter the number of fingers on a human hand:</p> <p>Enter the number of fingers on a human hand:</p>
<numericalresponse answer="5"> <numericalresponse answer="5">
<textline/> <formulaequationinput/>
</numericalresponse> </numericalresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
...@@ -114,7 +114,7 @@ class VerticalWithModulesFactory(object): ...@@ -114,7 +114,7 @@ class VerticalWithModulesFactory(object):
<problem display_name="Numerical Input" markdown=" " rerandomize="never" showanswer="finished"> <problem display_name="Numerical Input" markdown=" " rerandomize="never" showanswer="finished">
<p>Test numerical problem.</p> <p>Test numerical problem.</p>
<numericalresponse answer="5"> <numericalresponse answer="5">
<textline/> <formulaequationinput/>
</numericalresponse> </numericalresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
...@@ -129,7 +129,7 @@ class VerticalWithModulesFactory(object): ...@@ -129,7 +129,7 @@ class VerticalWithModulesFactory(object):
<problem display_name="Numerical Input" markdown=" " rerandomize="never" showanswer="finished"> <problem display_name="Numerical Input" markdown=" " rerandomize="never" showanswer="finished">
<p>Another test numerical problem.</p> <p>Another test numerical problem.</p>
<numericalresponse answer="5"> <numericalresponse answer="5">
<textline/> <formulaequationinput/>
</numericalresponse> </numericalresponse>
<solution> <solution>
<div class="detailed-solution"> <div class="detailed-solution">
......
describe("A jsinput has:", function () { xdescribe("A jsinput has:", function () {
beforeEach(function () { beforeEach(function () {
$('#fixture').remove(); $('#fixture').remove();
......
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 $this = $(this); // cache the jQuery object
var $preview = $("#" + this.id + "_preview");
var inputData = {
// These are the mutable values
lastSent: 0,
isWaitingForRequest: false,
requestVisible: 0,
errorDelayTimeout: null,
// The following don't change
// Find the URL from the closest parent problems-wrapper.
url: $this.closest('.problems-wrapper').data('url'),
// Grab the input id from the input.
inputId: $this.data('input-id'),
// Store the DOM/MathJax elements in which visible output occurs.
$preview: $preview,
// Note: sometimes MathJax hasn't finished loading yet.
jax: MathJax.Hub.getAllJax($preview[0])[0],
$img: $preview.find("img.loading"),
requestCallback: null // Fill it in in a bit.
};
// Give 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.on("input", initializeRequest);
// send an initial
MathJax.Hub.Queue(this, initializeRequest);
}
/**
* 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) {
results = MathJax.Hub.getAllJax(inputData.$preview[0]);
if (!results.length) {
console.log("Unable to find MathJax to display");
return;
}
inputData.jax = results[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.$img.css('visibility', 'visible');
inputData.errorWaitTimeout = window.setTimeout(function () {
display("\\text{" + response.error + "}");
inputData.$img.css('visibility', 'hidden');
}, formulaEquationPreview.errorDelay);
} else {
display(response.preview);
}
}
// Invoke the setup method.
$('.formulaequationinput input').each(setupInput);
};
formulaEquationPreview.enable();
...@@ -121,6 +121,7 @@ end ...@@ -121,6 +121,7 @@ end
static_js_dirs = Dir["common/lib/*"].select{|lib| File.directory?(lib)} static_js_dirs = Dir["common/lib/*"].select{|lib| File.directory?(lib)}
static_js_dirs << 'common/static/coffee' static_js_dirs << 'common/static/coffee'
static_js_dirs << 'common/static/js'
static_js_dirs.select!{|lib| !Dir["#{lib}/**/spec"].empty?} static_js_dirs.select!{|lib| !Dir["#{lib}/**/spec"].empty?}
static_js_dirs.each do |dir| 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