From 8ad4a477c15f928323da24aff4188bd7f0dca1d9 Mon Sep 17 00:00:00 2001 From: Jean-Michel Vedrine Date: Fri, 2 Feb 2018 16:53:59 +0100 Subject: Ajax dynamic formula display --- ajax.php | 55 +++++++++++++++++++++++++++ amd/build/display.min.js | 1 + amd/src/display.js | 28 ++++++++++++++ lang/en/qtype_algebra.php | 5 ++- renderer.php | 97 ++++++++++++++++++++++++++++++++++------------- settings.php | 7 ++++ styles.css | 10 +++++ version.php | 2 +- 8 files changed, 176 insertions(+), 29 deletions(-) create mode 100644 ajax.php create mode 100644 amd/build/display.min.js create mode 100644 amd/src/display.js diff --git a/ajax.php b/ajax.php new file mode 100644 index 0000000..156db22 --- /dev/null +++ b/ajax.php @@ -0,0 +1,55 @@ +. + +/** + * @package qtype_algebra + * @copyright 2018 Jean-Michel Vedrine + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define('AJAX_SCRIPT', true); + +require_once(__DIR__ . '/../../../config.php'); +require_once(__DIR__ . '/parser.php'); + +$p = new qtype_algebra_parser; + +$vars = required_param('vars', PARAM_RAW); +$expr = required_param('expr', PARAM_RAW); +/* +if (!confirm_sesskey()) { + header('HTTP/1.1 403 Forbidden'); + die(); +} +*/ +try { + $vars = explode(',', $vars); + if (empty($expr)) { + $texexp = ''; + } else { + $exp = $p->parse($expr, $vars); + $texexp = $exp->tex(); + } +} catch (Exception $e) { + $texexp = ''; +} +if ($CFG->qtype_algebra_texdelimiters == 'old') { + $texexp = '$$' . $texexp . '$$'; +} else { + $texexp = '\\[' . $texexp . '\\]'; +} +header('Content-Type: application/json; charset: utf-8'); +echo json_encode($texexp); diff --git a/amd/build/display.min.js b/amd/build/display.min.js new file mode 100644 index 0000000..cd5da13 --- /dev/null +++ b/amd/build/display.min.js @@ -0,0 +1 @@ +define(["jquery","core/config","core/notification"],function(a,b,c){return{init:function(){a(".algebra_answer").on("input paste keyup",null,null,function(){var d=a(this).attr("id"),e=d.replace(":","_"),f=a("#"+e+"_vars").html(),g=a(this).val(),h={vars:f,expr:g,sesskey:b.sesskey};a.post(b.wwwroot+"/question/type/algebra/ajax.php",h,null,"json").done(function(b){a("#"+e+"_display").html(''+b+""),MathJax.Hub.Queue(["Typeset",MathJax.Hub])}).fail(function(a,b,d){c.exception(d)})})}}}); \ No newline at end of file diff --git a/amd/src/display.js b/amd/src/display.js new file mode 100644 index 0000000..e2131b7 --- /dev/null +++ b/amd/src/display.js @@ -0,0 +1,28 @@ +define(['jquery', 'core/config', 'core/notification'], function($, config, notification) { + return { + init: function() { + $(".algebra_answer").on('input paste keyup', null, null, function() { + // Convert answer id to valid javascript name. + var id = $(this).attr('id'); + var display = id.replace(':', '_'); + var varnames = $('#' + display + '_vars').html(); + var currentanswer = $(this).val(); + var params = { + vars: varnames, + expr: currentanswer, + sesskey: config.sesskey, + }; + $.post(config.wwwroot + '/question/type/algebra/ajax.php', params, null, 'json') + .done(function(data) { + // Replace TeX form in page. + $('#' + display + '_display').html("" + data +""); + // MathJax update. + MathJax.Hub.Queue(["Typeset", MathJax.Hub]); + }) + .fail(function(jqXHR, status, error) { + notification.exception(error); + }); + }); + } + }; +}); \ No newline at end of file diff --git a/lang/en/qtype_algebra.php b/lang/en/qtype_algebra.php index 819e14b..4006858 100644 --- a/lang/en/qtype_algebra.php +++ b/lang/en/qtype_algebra.php @@ -145,4 +145,7 @@ $string['brackets'] = '\[...\]'; $string['invalidanswer'] = 'Invalid or unrecongnized answer'; $string['multiplyoperator'] = 'TeX operator for multiplication'; $string['times'] = '\\times'; -$string['cdot'] = '\\cdot'; \ No newline at end of file +$string['cdot'] = '\\cdot'; +$string['iframe'] = 'Using an iframe element'; +$string['dynamic'] = 'Using dynamic AJAX request'; +$string['formuladisplay'] = 'Method to display answer formula'; \ No newline at end of file diff --git a/renderer.php b/renderer.php index 529fb0e..ec3d57e 100644 --- a/renderer.php +++ b/renderer.php @@ -38,6 +38,9 @@ class qtype_algebra_renderer extends qtype_renderer { question_display_options $options) { global $CFG; + + $this->page->requires->js_call_amd('qtype_algebra/display', 'init'); + $question = $qa->get_question(); $currentanswer = $qa->get_last_qt_var('answer'); @@ -50,6 +53,7 @@ class qtype_algebra_renderer extends qtype_renderer { 'name' => $inputname, 'value' => $currentanswer, 'id' => $inputname, + 'class' => 'algebra_answer', 'size' => 80, ); @@ -65,13 +69,12 @@ class qtype_algebra_renderer extends qtype_renderer { } else { $fraction = 0; } - $inputattributes['class'] = $this->feedback_class($fraction); + $inputattributes['class'] = $this->feedback_class($fraction). ' algebra_answer'; $feedbackimg = $this->feedback_image($fraction); + } else { + $inputattributes['class'] = 'algebra_answer'; } - - $iframename = $nameprefix.'_if'; - // Name of the javascript function which causes the entered formula to be rendered. - $dfname = $nameprefix.'_display'; + // Create an array of variable names to use when displaying the function entered. $varnames = array(); if ($question and isset($question->variables)) { @@ -80,26 +83,14 @@ class qtype_algebra_renderer extends qtype_renderer { $varnames[] = $var->name; } } - $varnames = implode(',', $varnames); - // Javascript function which the button uses to display the rendering - // This function sents the source of the iframe to the 'displayformula.php' script giving - // it an argument of the formula entered by the student. - $displayfunction = 'function '.$dfname."() {\n". - ' var text="vars='.$varnames.'&expr="+escape(document.getElementsByName("'.$inputname.'")[0].value);'."\n". - " if(text.length != 0) {\n". - ' document.getElementsByName("'.$iframename.'")[0].src="'. - $CFG->wwwroot.'/question/type/algebra/displayformula.php?"+'. - 'text.replace(/\+/g,"%2b")'."\n". - " }\n". - " }\n"; $questiontext = $question->format_questiontext($qa); $input = html_writer::empty_tag('input', $inputattributes) . $feedbackimg; $result = html_writer::tag('div', $questiontext, array('class' => 'qtext')); - $result .= html_writer::tag('script', $displayfunction, array('type' => 'text/javascript')); + $result .= html_writer::start_tag('div', array('class' => 'ablock')); $result .= html_writer::start_tag('div', array('class' => 'prompt', 'style' => 'vertical-align: top')); if (isset($question->answerprefix) and !empty($question->answerprefix)) { @@ -118,15 +109,67 @@ class qtype_algebra_renderer extends qtype_renderer { $question->get_validation_error(array('answer' => $currentanswer)), array('class' => 'validationerror')); } - $result .= html_writer::start_tag('div', array('class' => 'dispresponse')); - $result .= html_writer::empty_tag('input', array('type' => 'button', - 'value' => get_string('displayresponse', 'qtype_algebra'), 'onclick' => $dfname.'()')); - $result .= html_writer::start_tag('iframe', - array('name' => $iframename, 'width' => '60%', 'height' => 60, 'align' => 'middle', 'src' => '')); - $result .= html_writer::end_tag('iframe'); - $result .= html_writer::tag('script', $dfname.'();', array('type' => 'text/javascript')); - $result .= html_writer::end_tag('div'); - + if (get_config('qtype_algebra', 'formuladisplay') == 'iframe') { + // Javascript function which the button uses to display the rendering + // This function sents the source of the iframe to the 'displayformula.php' script giving + // it an argument of the formula entered by the student. + $iframename = $nameprefix.'_if'; + // Name of the javascript function which causes the entered formula to be rendered. + $dfname = $nameprefix.'_display'; + $displayfunction = 'function '.$dfname."() {\n". + ' var text="vars='.$varnames.'&expr="+escape(document.getElementsByName("'.$inputname.'")[0].value);'."\n". + " if(text.length != 0) {\n". + ' document.getElementsByName("'.$iframename.'")[0].src="'. + $CFG->wwwroot.'/question/type/algebra/displayformula.php?"+'. + 'text.replace(/\+/g,"%2b")'."\n". + " }\n". + " }\n"; + $result .= html_writer::tag('script', $displayfunction, array('type' => 'text/javascript')); + $result .= html_writer::start_tag('div', array('class' => 'dispresponse')); + $result .= html_writer::empty_tag('input', array('type' => 'button', + 'value' => get_string('displayresponse', 'qtype_algebra'), 'onclick' => $dfname.'()')); + $result .= html_writer::start_tag('iframe', + array('name' => $iframename, 'width' => '60%', 'height' => 60, 'align' => 'middle', 'src' => '')); + $result .= html_writer::end_tag('iframe'); + $result .= html_writer::tag('script', $dfname.'();', array('type' => 'text/javascript')); + $result .= html_writer::end_tag('div'); + } else { + $result .= html_writer::tag('div', $varnames ,array( + 'type' => 'text', + 'name' => $nameprefix . '_vars', + 'id' => $nameprefix . '_vars', + 'size' => 80, + 'style' => 'display:none', + ) + ); + $p = new qtype_algebra_parser; + try { + $vars = explode(',', $varnames); + if (empty($currentanswer)) { + $texexp = ''; + } else { + $exp = $p->parse($currentanswer, $vars); + $texexp = $exp->tex(); + } + } catch (Exception $e) { + $texexp = ''; + } + if ($CFG->qtype_algebra_texdelimiters == 'old') { + $texexp = '$$' . $texexp . '$$'; + } else { + $texexp = '\\[' . $texexp . '\\]'; + } + $display = $question->format_text("" . $texexp ."", + 1 ,$qa, 'question', 'questiontext', $question->id); + $result .= html_writer::tag('div', $display ,array( + 'type' => 'text', + 'name' => $nameprefix . '_display', + 'id' => $nameprefix. '_display', + 'size' => 80, + 'class' => 'displayformula', + ) + ); + } return $result; } diff --git a/settings.php b/settings.php index 525324a..1c6fa30 100644 --- a/settings.php +++ b/settings.php @@ -54,4 +54,11 @@ if ($ADMIN->fulltree) { array('times' => new lang_string('times', 'qtype_algebra'), 'cdot' => new lang_string('cdot', 'qtype_algebra') ))); + // Method to diplay TeX formatted answer formula. + $settings->add(new admin_setting_configselect('qtype_algebra/formuladisplay', + new lang_string('formuladisplay', 'qtype_algebra'), + '', 'times', + array('iframe' => new lang_string('iframe', 'qtype_algebra'), + 'dynamic' => new lang_string('dynamic', 'qtype_algebra') + ))); } diff --git a/styles.css b/styles.css index 11b6fd7..7b90945 100644 --- a/styles.css +++ b/styles.css @@ -5,6 +5,16 @@ .que.algebra .answer input { width: 80%; } +.que.algebra .displayformula { + padding: 0.3em; + display: block; + width: 300px; + min-height: 70px ! important; + background: #fff3bf; + padding: 0.5em; + margin-top: 1em; + box-shadow: 0.5em 0.5em 1em #000; +} /* Editing form. */ body#page-question-type-algebra div[id^=fgroup_id_][id*=answeroptions_] { diff --git a/version.php b/version.php index 7eb6e41..1db1702 100644 --- a/version.php +++ b/version.php @@ -23,7 +23,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'qtype_algebra'; -$plugin->version = 2018010400; +$plugin->version = 2018020100; $plugin->requires = 2013050100; $plugin->release = '1.6 for Moodle 2.8, ... 3.5'; -- cgit v1.2.3