Skip to content
Snippets Groups Projects
Commit c7b03699 authored by smercuri's avatar smercuri
Browse files

init parsons block

parent 3fc95efe
Branches
No related tags found
No related merge requests found
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* A javascript module to handle the real-time validation of the input the student types
* into STACK questions.
*
* The overall way this works is as follows:
*
* - right at the end of this file are the init methods, which set things up.
* - The work common to all input types is done by StackInput.
* - Sending the Ajax request.
* - Updating the validation display.
* - The work specific to different input types (getting the content of the inputs) is done by
* the classes like
* - StackSimpleInput
* - StackTextareaInput
* - StackMatrixInput
* objects of these types need to implement the two methods addEventHandlers and getValue().
*
* @module qtype_stack/input
* @copyright 2018 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function helloWorld() {
print("Hello, world");
}
\ No newline at end of file
......@@ -847,6 +847,9 @@ $string['stackBlock_jsxgraph_overdefined_dimension'] = 'When defining aspect-rat
$string['stackBlock_jsxgraph_ref'] = 'The jsxgraph-block only supports referencing inputs present in the same CASText section \'{$a->var}\' does not exist here.';
$string['stackBlock_jsxgraph_param'] = 'The jsxgraph-block supports only these parameters in this context: {$a->param}.';
$string['stackBlock_parsons_width'] = 'The width of a Parsons block must use a known CSS-length unit.';
$string['stackBlock_parsons_height'] = 'The height of a Parsons block must use a known CSS-length unit.';
// Answer tests.
$string['stackOptions_AnsTest_values_AlgEquiv'] = "AlgEquiv";
$string['stackOptions_AnsTest_values_AlgEquivNouns'] = "AlgEquivNouns";
......
<?php
// This file is part of Stateful
//
// Stateful is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Stateful is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Stateful. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../block.interface.php');
require_once(__DIR__ . '/../block.factory.php');
require_once(__DIR__ . '/root.specialblock.php');
require_once(__DIR__ . '/stack_translate.specialblock.php');
require_once(__DIR__ . '/../../../../vle_specific.php');
require_once(__DIR__ . '/iframe.block.php');
stack_cas_castext2_iframe::register_counter('///PARSONS_COUNT///');
class stack_cas_castext2_parsons extends stack_cas_castext2_block {
/* This is not something we want people to edit in general. */
public static $namedversions = [
/* TODO: change to proof minimised scripts
*/
'cdn' => [
'css' => 'https://cdnjs.cloudflare.com/ajax/libs/jsxgraph/1.5.0/jsxgraph.min.css',
'js' => 'https://cdnjs.cloudflare.com/ajax/libs/jsxgraph/1.5.0/jsxgraphcore.min.js'],
'local' => [
'css' => 'cors://jsxgraph.min.css',
'js' => 'cors://jsxgraphcore.min.js',
]
];
public function compile($format, $options): ? MP_Node {
/* TODO : edit this compile function to turn JSON (decoded in PHP using JSON_decode) into
* relevant Maxima expression. For example, if JSON looks like {"step 1" : 5, "q" : 2, "c" : 1, "g" : 4, "f" : 3},
* then compile should give out proof(5, 2, 1, 4, 3) (i.e., MP_Identifier)
*/
$r = new MP_List([new MP_String('iframe')]);
// We need to transfer the parameters forward.
// Only the size parameters matter.
$xpars = [];
$inputs = []; // From inputname to variable name.
foreach ($this->params as $key => $value) {
if (substr($key, 0, 10) !== 'input-ref-') {
$xpars[$key] = $value;
} else {
$inputname = substr($key, 10);
$inputs[$inputname] = $value;
}
}
// These are some of the othe parameters we do not need to push forward.
if (isset($xpars['version'])) {
unset($xpars['version']);
}
if (isset($xpars['overridecss'])) {
unset($xpars['overridecss']);
}
if (isset($xpars['overridejs'])) {
unset($xpars['overridejs']);
}
// Disable scrolling for this.
$xpars['scrolling'] = false;
// Set a title.
$xpars['title'] = 'STACK Parsons ///PARSONS_COUNT///';
// Figure out what scripts we serve.
$css = self::$namedversions['local']['css'];
$js = self::$namedversions['local']['js'];
if (isset($this->params['version']) &&
isset(self::$namedversions[$this->params['version']])) {
$css = self::$namedversions[$this->params['version']]['css'];
$js = self::$namedversions[$this->params['version']]['js'];
}
if (isset($this->params['overridecss'])) {
$css = $this->params['overridecss'];
}
if (isset($this->params['overridejs'])) {
$js = $this->params['overridejs'];
}
$r->items[] = new MP_String(json_encode($xpars));
// Plug in some style and scripts.
$mathjax = stack_get_mathjax_url();
// Silence the MathJax message that blinks on top of every graph.
$r->items[] = new MP_List([
new MP_String('script'),
new MP_String(json_encode(['type' => 'text/x-mathjax-config'])),
new MP_String('MathJax.Hub.Config({messageStyle: "none"});')
]);
$r->items[] = new MP_List([
new MP_String('script'),
new MP_String(json_encode(['type' => 'text/javascript', 'src' => $mathjax]))
]);
$r->items[] = new MP_List([
new MP_String('style'),
new MP_String(json_encode(['href' => $css]))
]);
$r->items[] = new MP_List([
new MP_String('script'),
new MP_String(json_encode(['type' => 'text/javascript', 'src' => $js]))
]);
// We need to define a size for the inner content.
$width = '500px';
$height = '400px';
$aspectratio = false;
if (array_key_exists('width', $xpars)) {
$width = $xpars['width'];
}
if (array_key_exists('height', $xpars)) {
$height = $xpars['height'];
}
$astyle = "width:calc($width - 3px);height:calc($height - 3px);";
if (array_key_exists('aspect-ratio', $xpars)) {
$aspectratio = $xpars['aspect-ratio'];
// Unset the undefined dimension, if both are defined then we have a problem.
if (array_key_exists('height', $xpars)) {
$astyle = "height:calc($height - 3px);aspect-ratio:$aspectratio;";
} else if (array_key_exists('width', $xpars)) {
$astyle = "width:calc($width - 3px);aspect-ratio:$aspectratio;";
}
}
// Add the div to the doc.
// Note that we have two divs, the exterior one defines the size
// and the interior one contains the graph.
// TODO :change div class?
$r->items[] = new MP_String('<div style="' . $astyle .
'"><div class="jxgbox" id="jxgbox" style="width:100%;height:100%;"></div></div><script type="module">');
// For binding we need to import the binding libraries.
$r->items[] = new MP_String("\nimport {stack_js} from '" . stack_cors_link('stackjsiframe.min.js') . "';\n");
$r->items[] = new MP_String("import {stack_jxg} from '" . stack_cors_link('stackjsxgraph.min.js') . "';\n");
// Do we need to bind anything?
if (count($inputs) > 0) {
// Then we need to link up to the inputs.
$promises = [];
$vars = [];
foreach ($inputs as $key => $value) {
// That true there makes us sync input-events as well, like we did before.
$promises[] = 'stack_js.request_access_to_input("' . $key . '",true)';
$vars[] = $value;
}
$linkcode = 'Promise.all([' . implode(',', $promises) . '])';
$linkcode .= '.then(([' . implode(',', $vars) . ']) => {' . "\n";
$r->items[] = new MP_String($linkcode);
}
// Plug in the div id = board id thing.
$r->items[] = new MP_String('var divid = "jxgbox";var BOARDID = divid;');
$opt2 = [];
if ($options !== null) {
$opt2 = array_merge([], $options);
}
$opt2['in iframe'] = true;
foreach ($this->children as $item) {
// Assume that all code inside is JavaScript and that we do not
// want to do the markdown escaping or any other in it.
$c = $item->compile(castext2_parser_utils::RAWFORMAT, $opt2);
if ($c !== null) {
$r->items[] = $c;
}
}
if (count($inputs) > 0) {
// Close the `then(`.
$r->items[] = new MP_String("\n});");
}
// In the end close the script tag.
$r->items[] = new MP_String('</script>');
return $r;
}
public function is_flat() : bool {
// Even when the content were flat we need to evaluate this during postprocessing.
return false;
}
public function postprocess(array $params, castext2_processor $processor): string {
return 'This is never happening! The logic goes to [[iframe]].';
}
public function validate_extract_attributes(): array {
return [];
}
public function validate(
&$errors = [],
$options = []
): bool {
// Basically, check that the dimensions have units we know.
// Also that the references make sense.
$valid = true;
$width = '500px';
$height = '400px';
if (array_key_exists('width', $this->params)) {
$width = $this->params['width'];
}
if (array_key_exists('height', $this->params)) {
$height = $this->params['height'];
}
// NOTE! List ordered by length. For the trimming logic.
$validunits = ['vmin', 'vmax', 'rem', 'em', 'ex', 'px', 'cm', 'mm',
'in', 'pt', 'pc', 'ch', 'vh', 'vw', '%'];
$widthend = false;
$heightend = false;
$widthtrim = $width;
$heighttrim = $height;
foreach ($validunits as $suffix) {
if (!$widthend && strlen($width) > strlen($suffix) &&
substr($width, -strlen($suffix)) === $suffix) {
$widthend = true;
$widthtrim = substr($width, 0, -strlen($suffix));
}
if (!$heightend && strlen($height) > strlen($suffix) &&
substr($height, -strlen($suffix)) === $suffix) {
$heightend = true;
$heighttrim = substr($height, 0, -strlen($suffix));
}
if ($widthend && $heightend) {
break;
}
}
$err = [];
if (!$widthend) {
$valid = false;
$err[] = stack_string('stackBlock_jsxgraph_width');
}
if (!$heightend) {
$valid = false;
$err[] = stack_string('stackBlock_jsxgraph_height');
}
if (!preg_match('/^[0-9]*[\.]?[0-9]+$/', $widthtrim)) {
$valid = false;
$err[] = stack_string('stackBlock_jsxgraph_width_num');
}
if (!preg_match('/^[0-9]*[\.]?[0-9]+$/', $heighttrim)) {
$valid = false;
$err[] = stack_string('stackBlock_jsxgraph_height_num');
}
if (array_key_exists('width', $this->params) &&
array_key_exists('height', $this->params) &&
array_key_exists('aspect-ratio', $this->params)) {
$valid = false;
$err[] = stack_string('stackBlock_jsxgraph_overdefined_dimension');
}
if (!(array_key_exists('width', $this->params) ||
array_key_exists('height', $this->params)) &&
array_key_exists('aspect-ratio', $this->params)) {
$valid = false;
$err[] = stack_string('stackBlock_jsxgraph_underdefined_dimension');
}
if (array_key_exists('version', $this->params) && array_key_exists($this->params['version'], self::$namedversions)) {
$valid = false;
$err[] = stack_string('stackBlock_jsxgraph_unknown_named_version');
}
$valids = null;
foreach ($this->params as $key => $value) {
if (substr($key, 0, 10) === 'input-ref-') {
$varname = substr($key, 10);
if (isset($options['inputs']) && !isset($options['inputs'][$varname])) {
$err[] = stack_string('stackBlock_jsxgraph_input_missing',
['var' => $varname]);
}
} else if ($key !== 'width' && $key !== 'height' && $key !== 'aspect-ratio' &&
$key !== 'version' && $key !== 'overridejs' && $key !== 'overridecss') {
$err[] = "Unknown parameter '$key' for jsxgraph-block.";
$valid = false;
if ($valids === null) {
$valids = ['width', 'height', 'aspect-ratio', 'version', 'overridecss', 'overridejs'];
// The variable $inputdefinitions is not defined!
if ($inputdefinitions !== null) {
$tmp = $root->get_parameter('ioblocks');
$inputs = [];
foreach ($inputdefinitions->get_names() as $key) {
$inputs[] = "input-ref-$key";
}
$valids = array_merge($valids, $inputs);
}
$err[] = stack_string('stackBlock_jsxgraph_param', [
'param' => implode(', ', $valids)]);
}
}
}
// Wrap the old string errors with the context details.
foreach ($err as $er) {
$errors[] = new $options['errclass']($er, $options['context'] . '/' . $this->position['start'] . '-' .
$this->position['end']);
}
return $valid;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment