MDL-79863 qtype_ordering: Moodle.2x question/type/ordering use md5 instead of fraction field as id in ordering questions

This commit is contained in:
Gordon Bateson
2024-04-04 10:59:13 +08:00
committed by Mathew May
parent 2da308c7dd
commit 05a43d9094
4 changed files with 283 additions and 311 deletions
+26 -29
View File
@@ -34,6 +34,11 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_ordering_edit_form extends question_edit_form {
public function qtype() {
return 'ordering';
}
/**
* Add question-type specific form fields.
*
@@ -43,28 +48,35 @@ class qtype_ordering_edit_form extends question_edit_form {
$NUMANS_START = 10;
$NUMANS_ADD = 5;
$menu = array(get_string('ordering_exactorder', 'qtype_ordering'), get_string('ordering_relativeorder', 'qtype_ordering'), get_string('ordering_contiguous', 'qtype_ordering'));
$mform->addElement('select', 'logical', get_string('ordering_logicalpossibilities', 'qtype_ordering'), $menu);
$options = array(
0 => get_string('ordering_exactorder', 'qtype_ordering'), // = all ?
1 => get_string('ordering_relativeorder', 'qtype_ordering'), // = random subset
2 => get_string('ordering_contiguous', 'qtype_ordering') // = contiguous subset
);
$mform->addElement('select', 'logical', get_string('ordering_logicalpossibilities', 'qtype_ordering'), $options);
$mform->setDefault('logical', 0);
$menu1[] = "All";
$options = array(0 => 'All');
for ($i=3; $i <= 20; $i++) {
$menu1[] = $i;
$options[] = $i;
}
$mform->addElement('select', 'studentsee', get_string('ordering_itemsforstudent', 'qtype_ordering'), $menu1);
$mform->addElement('select', 'studentsee', get_string('ordering_itemsforstudent', 'qtype_ordering'), $options);
$mform->setDefault('studentsee', 0);
$repeated = array();
$repeated[] =& $mform->createElement('header', 'choicehdr', get_string('ordering_choiceno', 'qtype_ordering', '{no}'));
$repeated[] =& $mform->createElement('textarea', 'answer', get_string('ordering_answer', 'qtype_ordering'), 'rows="3" cols="50"');
if (isset($this->question->options)){
$countanswers = count($this->question->options->answers);
} else {
if (empty($this->question->options)){
$countanswers = 0;
} else {
$countanswers = count($this->question->options->answers);
}
if ($NUMANS_START > ($countanswers + $NUMANS_ADD)) {
$repeatsatstart = $NUMANS_START;
} else {
$repeatsatstart = ($countanswers + $NUMANS_ADD);
}
$repeatsatstart = ($NUMANS_START > ($countanswers + $NUMANS_ADD))?
$NUMANS_START : ($countanswers + $NUMANS_ADD);
$repeatedoptions = array();
$repeatedoptions['fraction']['default'] = 0;
$mform->setType('answer', PARAM_NOTAGS);
@@ -109,27 +121,16 @@ class qtype_ordering_edit_form extends question_edit_form {
}
//echo "data_preprocessing";
//print_r ($question);
//die();
return $question;
//return true;
}
public function validation($data, $files) {
$errors = array();
$answers = $data['answer'];
$answercount = 0;
$totalfraction = 0;
$maxfraction = -1;
foreach ($answers as $key => $answer){
//check no of choices
$trimmedanswer = trim($answer);
if (!empty($trimmedanswer)){
foreach ($data['answer'] as $answer){
$answer = trim($answer);
if ($answer || $answer==='0'){
$answercount++;
}
}
@@ -144,8 +145,4 @@ class qtype_ordering_edit_form extends question_edit_form {
return $errors;
}
public function qtype() {
return 'ordering';
}
}
+80 -118
View File
@@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Multiple choice question definition classes.
* Ordering question definition classes.
*
* @package qtype
* @subpackage ordering
@@ -26,16 +26,11 @@
defined('MOODLE_INTERNAL') || die();
/**
* Base class for multiple choice questions. The parts that are common to
* single select and multiple select.
* Represents an ordering question.
*
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class qtype_ordering_base extends question_graded_automatically {
}
class qtype_ordering_question extends question_graded_automatically {
public $rightanswer;
public $truefeedback;
@@ -52,140 +47,107 @@ class qtype_ordering_question extends question_graded_automatically {
}
public function summarise_response(array $response) {
}
public function classify_response(array $response) {
print_r ($response);
echo "!2!";
echo 'STOP in question/type/ordering/question.php classify_response()';
die();
if (!array_key_exists('answer', $response)) {
return array($this->id => question_classified_response::no_response());
}
list($fraction) = $this->grade_response($response);
if ($response['answer']) {
return array($this->id => new question_classified_response(1,
get_string('true', 'qtype_ordering'), $fraction));
if (array_key_exists('answer', $response)) {
$responseclassid = ($response['answer'] ? 1 : 0);
list($fraction) = $this->grade_response($response);
return array($this->id => new question_classified_response($responseclassid, get_string('true', 'qtype_ordering'), $fraction));
} else {
return array($this->id => new question_classified_response(0,
get_string('false', 'qtype_ordering'), $fraction));
return array($this->id => question_classified_response::no_response());
}
}
public function is_complete_response(array $response) {
global $DB;
global $CFG, $DB;
$answer = $_POST["q".$this->id];
$orderdata = explode(",", $answer);
$responses = array();
$erroranswer = array();
$responses = explode(',', $_POST['q'.$this->id]);
$responses = array_filter($responses); // remove blanks
$responses = array_unique($responses); // remove duplicates
foreach ($orderdata as $orderdata_) {
if (!empty($orderdata_))
list($a1,$a2,$responses[]) = explode("_", $orderdata_);
}
//print_r($responses);
$raw_grade = 0;
$questionordering = $DB->get_record ("question_ordering", array("question" => $this->id));
$questionanswers = $DB->get_records ("question_answers", array("question" => $this->id), "id");
$fractioncount = 1;
foreach ($questionanswers as $questionanswersfractiontemp) {
$questionanswers[$questionanswersfractiontemp->id]->fraction = $fractioncount;
++$fractioncount;
}
foreach ($questionanswers as $questionanswers_) {
$questionanswersfraction[$questionanswers_->fraction] = $questionanswers_->id;
}
$erroranswer['main'] = 0;
if (is_array($responses)) {
if ($questionordering->logical == 0) {
$nefr = 1;
if ($questionordering->studentsee != 0) {
foreach ($questionanswersfraction as $frkey => $frvalue) {
if (in_array($frvalue, $responses)) {
$questionanswersfractionnew [$nefr] = $frvalue;
$nefr ++;
}
}
$questionanswersfraction = $questionanswersfractionnew;
}
for ($i = 0; $i <= count($responses) - 1; $i++) {
if ($responses[$i] != $questionanswersfraction[$i + 1]) {
$erroranswer['main'] ++;
}
}
} else if ($questionordering->logical == 1) {
$erroranswer['main'] = 0;
for ($i = 0; $i <= count($responses) - 1; $i++) {
if ($i != count($responses) -1) {
if ($questionanswers[$responses[$i]]->fraction > $questionanswers[$responses[$i + 1]]->fraction) {
$erroranswer['main'] ++;
}
}
}
} else if ($questionordering->logical == 2) {
$fruits = $responses;
sort($fruits);
reset($fruits);
if ($responses[0] != $fruits[0]) {
$erroranswer['main'] ++;
}
for ($i = 0; $i <= count($responses) - 1; $i++) {
if ($i != count($responses) -1) {
if ($questionanswers[$responses[$i]]->fraction < $questionanswers[$responses[$i + 1]]->fraction) {
$raznica = $questionanswers[$responses[$i + 1]]->fraction - $questionanswers[$responses[$i]]->fraction - 1;
if ($raznica >= 1) {
$alreadycount = "false";
for ($d = 1; $d <= $raznica; $d++) {
if ($alreadycount == "false") {
if (in_array($questionanswersfraction[$questionanswers[$responses[$i]]->fraction + $d] ,$responses)) {
$alreadycount = "true";
$erroranswer['main'] ++;
}
}
}
}
} else {
$erroranswer['main'] ++;
}
}
}
foreach ($responses as $i => $response) {
if (substr($response, 0, 14)=='ordering_item_') {
$responses[$i] = substr($response, 14);
} else {
unset($responses[$i]); // remove invalid response
}
}
if (! $options = $DB->get_record ('question_ordering', array('question' => $this->id))) {
$options = (object)array('logical' => 0); // shouldn't happen !!
}
if (! $answers = $DB->get_records ('question_answers', array('question' => $this->id), 'fraction')) {
$answers = array(); // shouldn't happen !!
}
if ($questionordering->logical == 0 || $questionordering->logical == 2)
$allcount = count($responses);
else
$allcount = count($responses) - 1;
$grade = ($allcount - $erroranswer['main']) / $allcount;
if ($options->logical==0) {
$total = count($answers); // require all answers
} else {
$state->raw_grade = 0;
$state->grade = 0;
$total = $options->studentsee + 2; // a subset of answers
}
$validresponses = array();
foreach ($answers as $answerid => $answer) {
$response = md5($CFG->passwordsaltmain.$answer->answer);
$sortorder = intval($answer->fraction);
if (in_array($response, $responses)) {
$answers[$answerid]->sortorder = $sortorder;
$answers[$answerid]->response = $response;
$validresponses[] = $response;
} else {
unset($answers[$answerid]); // this answer is not used
}
}
// convert $answers to sequentially numbered array
$answers = array_values($answers);
// sort $answers by sortorder (not really necessary)
usort($answers, array($this, 'usort_sortorder'));
// remove invalid responses
foreach ($responses as $i => $response) {
if (! in_array($response, $validresponses)) {
unset($responses[$i]);
}
}
unset($validresponses);
$correct = 0;
foreach ($answers as $i => $answer) {
if (isset($responses[$i]) && $answer->response==$responses[$i]) {
$correct++;
}
$i++;
}
if ($total==0) {
$grade = 0;
} else {
$grade = round($correct / $total, 5);
}
$_SESSION['SESSION']->quiz_answer['q'.$this->id] = $grade;
return true;
}
public function usort_sortorder($a, $b) {
if ($a->sortorder < $b->sortorder) {
return -1;
}
if ($a->sortorder > $b->sortorder) {
return 1;
}
return 0; // equal values
}
public function is_gradable_response(array $response) {
return true;
}
+67 -93
View File
@@ -34,140 +34,116 @@ if (class_exists('question_type')) {
}
/**
* The multiple choice question type.
* The ordering question type.
*
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_ordering extends question_type {
/*
public function get_question_options($question) {
global $DB, $OUTPUT;
$question->options = $DB->get_record('question_ordering',
array('question' => $question->id), '*', MUST_EXIST);
parent::get_question_options($question);
public function is_not_blank($value) {
$value = trim($value);
return ($value || $value==='0');
}
*/
public function save_question_options($question) {
global $DB, $OUTPUT;
global $DB;
foreach ($question->answer as $answerkey => $answervalue) {
$question->fraction[] = $answerkey + 1;
}
$result = new stdClass();
$result = new stdClass;
if (!$oldanswers = $DB->get_records('question_answers', array('question'=>$question->id), 'id ASC')) {
$oldanswers = array();
}
// remove empty answers
$question->answer = array_filter($question->answer, array($this, 'is_not_blank'));
$question->answer = array_values($question->answer); // make keys sequential
// following hack to check at least two answers exist
$answercount = 0;
foreach ($question->answer as $key=>$dataanswer) {
if ($dataanswer != "") {
$answercount++;
}
}
$answercount += count($oldanswers);
if ($answercount < 2) { // check there are at lest 2 answers for multiple choice
$result->notice = get_string("ordering_notenoughanswers", "qtype_ordering", "2");
// count how many answers we have
$countanswers = count($question->answer);
// check at least two answers exist
if ($countanswers < 2) {
$result->notice = get_string('ordering_notenoughanswers', 'qtype_ordering', '2');
return $result;
}
$question->feedback = range(1, $countanswers);
if ($answerids = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC', 'id,question')) {
$answerids = array_keys($answerids);
} else {
$answerids = array();
}
// Insert all the new answers
$totalfraction = 0;
$maxfraction = -1;
foreach ($question->answer as $i => $text) {
$answer = (object)array(
'question' => $question->id,
'fraction' => ($i + 1), // start at 1
'answer' => $text,
'answerformat' => FORMAT_MOODLE, // =0
'feedback' => '',
'feedbackformat' => FORMAT_MOODLE, // =0
);
$answers = array();
foreach ($question->answer as $key => $dataanswer) {
if ($dataanswer != "") {
if ($answer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer->answer = $dataanswer;
$answer->fraction = $question->fraction[$key];
if (!$DB->update_record("question_answers", $answer)) {
$result->error = "Could not update quiz answer! (id=$answer->id)";
return $result;
}
} else {
unset($answer);
$answer->answer = $dataanswer;
$answer->question = $question->id;
$answer->fraction = number_format($question->fraction[$key], 7);
$answer->feedback = "";
//echo '<pre>';
//print_r ($answer);
if (!$answer->id = $DB->insert_record("question_answers", $answer)) {
$result->error = "Could not insert quiz answer! ";
return $result;
}
if ($answer->id = array_shift($answerids)) {
if (! $DB->update_record('question_answers', $answer)) {
$result->error = 'Could not update quiz answer! (id='.$answer->id.')';
return $result;
}
$answers[] = $answer->id;
if ($question->fraction[$key] > 0) { // Sanity checks
$totalfraction += $question->fraction[$key];
}
if ($question->fraction[$key] > $maxfraction) {
$maxfraction = $question->fraction[$key];
} else {
if (! $answer->id = $DB->insert_record('question_answers', $answer)) {
$result->error = 'Could not insert quiz answer! ';
return $result;
}
}
}
$update = true;
$options = $DB->get_record("question_ordering", array("question" => $question->id));
if (!$options) {
$update = false;
$options = new stdClass;
$options->question = $question->id;
// create $options for this ordering question
$options = (object)array(
'question' => $question->id,
'logical' => $question->logical,
'studentsee' => $question->studentsee,
'correctfeedback' => $question->correctfeedback,
'incorrectfeedback' => $question->incorrectfeedback,
'partiallycorrectfeedback' => $question->partiallycorrectfeedback
);
}
$options->logical = $question->logical;
$options->studentsee = $question->studentsee;
$options->correctfeedback = $question->correctfeedback;
$options->partiallycorrectfeedback = $question->partiallycorrectfeedback;
$options->incorrectfeedback = $question->incorrectfeedback;
if ($update) {
if (!$DB->update_record("question_ordering", $options)) {
$result->error = "Could not update quiz ordering options! (id=$options->id)";
// add/update $options for this ordering question
if ($options->id = $DB->get_field('question_ordering', 'id', array('question' => $question->id))) {
if (! $DB->update_record('question_ordering', $options)) {
$result->error = 'Could not update quiz ordering options! (id='.$options->id.')';
return $result;
}
} else {
if (!$DB->insert_record("question_ordering", $options)) {
$result->error = "Could not insert quiz ordering options!";
if (! $options->id = $DB->insert_record('question_ordering', $options)) {
$result->error = 'Could not insert question ordering options!';
return $result;
}
}
// delete old answer records
if (!empty($oldanswers)) {
foreach($oldanswers as $oa) {
$DB->delete_records('question_answers', array('id' => $oa->id));
}
// delete old answer records, if any
if (count($answerids)) {
$DB->delete_records_list('question_answers', 'id', $answerids);
}
/// Perform sanity checks on fractional grades
return true;
}
public function get_question_options($question) {
global $DB, $OUTPUT;
// Get additional information from database
// and attach it to the question object
if (!$question->options = $DB->get_record('question_ordering',
array('question' => $question->id))) {
// load the options
if (! $question->options = $DB->get_record('question_ordering', array('question' => $question->id))) {
echo $OUTPUT->notification('Error: Missing question options!');
return false;
}
// Load the answers
if (!$question->options->answers = $DB->get_records('question_answers',
array('question' => $question->id), 'id ASC')) {
echo $OUTPUT->notification('Error: Missing question answers for truefalse question ' .
$question->id . '!');
// Load the answers - "fraction" is used to signify the order of the answers
if (! $question->options->answers = $DB->get_records('question_answers', array('question' => $question->id), 'fraction ASC')) {
echo $OUTPUT->notification('Error: Missing question answers for ordering question ' . $question->id . '!');
return false;
}
//parent::get_question_options($question);
return true;
}
/*
@@ -195,10 +171,8 @@ class qtype_ordering extends question_type {
public function delete_question($questionid, $contextid) {
global $DB;
$DB->delete_records('question_ordering', array('question' => $questionid));
parent::delete_question($questionid, $contextid);
}
}
// NEW LINES START
+110 -71
View File
@@ -34,121 +34,160 @@ defined('MOODLE_INTERNAL') || die();
class qtype_ordering_renderer extends qtype_renderer {
public function formulation_and_controls(question_attempt $qa, question_display_options $options) {
global $DB, $CFG;
global $CFG, $DB;
static $addStyle = true;
static $addScript = true;
$question = $qa->get_question();
if (! $ordering = $DB->get_record('question_ordering', array('question' => $question->id))) {
if (! $options = $DB->get_record('question_ordering', array('question' => $question->id))) {
return '';
}
if (empty($ordering->studentsee)) {
$ordering->studentsee = 100;
} else {
$ordering->studentsee += 2;
}
if (! $answers = $DB->get_records('question_answers', array('question' => $question->id), '', '*', 0, $ordering->studentsee)) {
if (! $answers = $DB->get_records('question_answers', array('question' => $question->id), '', '*')) {
return ''; // shouldn't happen !!
}
shuffle($answers);
if ($options->studentsee==0) { // all items
$options->studentsee = count($answers);
} else {
// a nasty hack so that "studentsee" is the same
// as what is displayed by edit_ordering_form.php
$options->studentsee += 2;
}
switch ($options->logical) {
case 0: // all
$answerids = array_keys($answers);
break;
case 1: // random subset
$answerids = array_rand($answers, $options->studentsee);
break;
case 2: // contiguous subset
if (count($answers) > $options->studentsee) {
$offset = mt_rand(0, count($answers) - $options->studentsee);
$answers = array_slice($answers, $offset, $options->studentsee, true);
}
$answerids = array_keys($answers);
break;
}
shuffle($answerids);
$result = '';
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/jquery.js'));
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/jquery-ui.js'));
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery.js'));
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery-ui.js'));
$style = "\n";
$style .= "ul.sortable li {\n";
$style .= "ul.sortable".$question->id." li {\n";
$style .= " position: relative;\n";
$style .= "}\n";
$style .= "ul.boxy {\n";
$style .= " border: 1px solid #ccc;\n";
$style .= " float: left;\n";
$style .= " font-family: Arial, sans-serif;\n";
$style .= " font-size: 13px;\n";
$style .= " list-style-type: none;\n";
$style .= " margin: 0px;\n";
$style .= " margin-left: 5px;\n";
$style .= " padding: 4px 4px 0 4px;\n";
$style .= " width: 360px;\n";
$style .= "}\n";
$style .= "ul.boxy li {\n";
$style .= " background-color: #eeeeee;\n";
$style .= " border: 1px solid #cccccc;\n";
$style .= " border-image: initial;\n";
$style .= " cursor: move;\n";
$style .= " list-style-type: none;\n";
$style .= " margin-bottom: 1px;\n";
$style .= " min-height: 20px;\n";
$style .= " padding: 8px 2px;\n";
$style .= "}\n";
if ($addStyle) {
$addStyle = false; // only add style once
$style .= "ul.boxy {\n";
$style .= " border: 1px solid #ccc;\n";
$style .= " float: left;\n";
$style .= " font-family: Arial, sans-serif;\n";
$style .= " font-size: 13px;\n";
$style .= " list-style-type: none;\n";
$style .= " margin: 0px;\n";
$style .= " margin-left: 5px;\n";
$style .= " padding: 4px 4px 0 4px;\n";
$style .= " width: 360px;\n";
$style .= "}\n";
$style .= "ul.boxy li {\n";
$style .= " background-color: #eeeeee;\n";
$style .= " border: 1px solid #cccccc;\n";
$style .= " border-image: initial;\n";
$style .= " cursor: move;\n";
$style .= " list-style-type: none;\n";
$style .= " margin-bottom: 1px;\n";
$style .= " min-height: 20px;\n";
$style .= " padding: 8px 2px;\n";
$style .= "}\n";
}
$result .= html_writer::tag('style', $style, array('type' => 'text/css'));
$script = "\n";
$script .= "//<![CDATA[\n";
$script .= "$(function() {\n";
$script .= " $('#sortable').sortable({\n";
$script .= " $('#sortable".$question->id."').sortable({\n";
$script .= " update: function(event, ui) {\n";
$script .= " var ItemsOrder = $(this).sortable('toArray').toString();\n";
$script .= " $('#q".$question->id."').attr('value', ItemsOrder);\n";
$script .= " }\n";
$script .= " });\n";
$script .= " $('#sortable').disableSelection();\n";
$script .= " $('#sortable".$question->id."').disableSelection();\n";
$script .= "});\n";
$script .= "$(document).ready(function() {\n";
$script .= " var ItemsOrder = $('#sortable').sortable('toArray').toString();\n";
$script .= " var ItemsOrder = $('#sortable".$question->id."').sortable('toArray').toString();\n";
$script .= " $('#q".$question->id."').attr('value', ItemsOrder);\n";
$script .= "});\n";
$script .= "//]]>\n";
$result .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
$result .= html_writer::tag('div', stripslashes($question->format_questiontext($qa)), array('class' => 'qtext'));
$result .= html_writer::start_tag('div', array('class' => 'ablock'));
$result .= html_writer::start_tag('div', array('class' => 'answer'));
$result .= html_writer::start_tag('ul', array('class' => 'boxy', 'id' => 'sortable'));
$result .= html_writer::start_tag('ul', array('class' => 'boxy', 'id' => 'sortable'.$question->id));
foreach ($answers as $answer) {
// $answer->fraction holds the correct order - as a decimal ?!
$id = 'ordering_item_'.$answer->id.'_'.intval($answer->fraction);
// generate ordering items
foreach ($answerids as $i => $answerid) {
// the original "id" revealed the correct order of the answers
// because $answer->fraction holds the correct order number
// $id = 'ordering_item_'.$answerid.'_'.intval($answers[$answerid]->fraction);
$id = 'ordering_item_'.md5($CFG->passwordsaltmain.$answers[$answerid]->answer);
$params = array('class' => 'ui-state-default', 'id' => $id);
$result .= html_writer::tag('li', stripslashes($answer->answer), $params);
$result .= html_writer::tag('li', $answers[$answerid]->answer, $params);
}
$result .= html_writer::end_tag('ul');
$result .= html_writer::end_tag('div'); // answer
$result .= html_writer::end_tag('div'); // ablock
$result .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'q'.$question->id, 'id' => 'q'.$question->id));
$result .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'answer', 'value' => '712'));
$result .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'q'.$question->id, 'id' => 'q'.$question->id, 'value' => '9'));
$result .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'answer', 'value' => ''));
$result .= html_writer::tag('div', '', array('style' => 'clear:both;'));
$script = "\n";
$script .= "function orderingTouchHandler(event) {\n";
$script .= " var touch = event.changedTouches[0];\n";
$script .= " switch (event.type) {\n";
$script .= " case 'touchstart': var type = 'mousedown'; break;\n";
$script .= " case 'touchmove': var type = 'mousemove'; event.preventDefault(); break;\n";
$script .= " case 'touchend': var type = 'mouseup'; break;\n";
$script .= " default: return;\n";
$script .= " }\n";
$script .= " var simulatedEvent = document.createEvent('MouseEvent');\n";
$script .= " //initMouseEvent(type, canBubble, cancelable, view, clickCount, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);\n";
$script .= " simulatedEvent.initMouseEvent(type, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0/*left*/, null);\n";
$script .= " touch.target.dispatchEvent(simulatedEvent);\n";
$script .= " event.preventDefault();\n";
$script .= "}\n";
$script .= "function orderingInit() {\n";
$script .= " var obj = document.getElementById('sortable');\n";
$script .= " if (obj) {\n";
$script .= " for (var i=0; i<obj.childNodes.length; i++) {\n";
$script .= " obj.childNodes.item(i).addEventListener('touchstart', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchmove', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchend', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchcancel', orderingTouchHandler, false);\n";
$script .= " }\n";
$script .= " } else {\n";
$script .= " setTimeout(orderingInit, 500);\n"; // try again in 1/2 a second
$script .= " }\n";
$script .= "}\n";
$script .= "orderingInit();\n";
$script .= "//<![CDATA[\n";
if ($addScript) {
$addScript = false; // only add these functions once
$script .= "function orderingTouchHandler(event) {\n";
$script .= " var touch = event.changedTouches[0];\n";
$script .= " switch (event.type) {\n";
$script .= " case 'touchstart': var type = 'mousedown'; break;\n";
$script .= " case 'touchmove': var type = 'mousemove'; event.preventDefault(); break;\n";
$script .= " case 'touchend': var type = 'mouseup'; break;\n";
$script .= " default: return;\n";
$script .= " }\n";
$script .= " var simulatedEvent = document.createEvent('MouseEvent');\n";
$script .= " // initMouseEvent(type, canBubble, cancelable, view, clickCount, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget)\n";
$script .= " simulatedEvent.initMouseEvent(type, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);\n";
$script .= " touch.target.dispatchEvent(simulatedEvent);\n";
$script .= " event.preventDefault();\n";
$script .= "}\n";
$script .= "function orderingInit(sortableid) {\n";
$script .= " var obj = document.getElementById(sortableid);\n";
$script .= " if (obj) {\n";
$script .= " for (var i=0; i<obj.childNodes.length; i++) {\n";
$script .= " obj.childNodes.item(i).addEventListener('touchstart', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchmove', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchend', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchcancel', orderingTouchHandler, false);\n";
$script .= " }\n";
$script .= " obj = null;\n";
$script .= " } else {\n";
$script .= " // try again in 1/2 a second - shouldn't be necessary !!\n";
$script .= " setTimeout(new Function('orderingInit(".'"'."'+sortableid+'".'"'.")'), 500);\n";
$script .= " }\n";
$script .= "}\n";
}
$script .= "orderingInit('sortable".$question->id."');\n";
$script .= "//]]>\n";
$result .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
return $result;