MDL-78662 ddimageortext: Allow answer to support filter dynamic content.

This commit is contained in:
hieuvu
2024-05-07 08:57:26 +07:00
parent 6d34386ca9
commit f1044b45bd
7 changed files with 163 additions and 21 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+95 -15
View File
@@ -24,12 +24,14 @@ define([
'jquery',
'core/dragdrop',
'core/key_codes',
'core_form/changechecker'
'core_form/changechecker',
'core_filters/events',
], function(
$,
dragDrop,
keys,
FormChangeChecker
FormChangeChecker,
filterEvent
) {
"use strict";
@@ -45,6 +47,7 @@ define([
function DragDropOntoImageQuestion(containerId, readOnly, places) {
this.containerId = containerId;
this.questionAnswer = {};
this.questionDragDropWidthHeight = [];
M.util.js_pending('qtype_ddimageortext-init-' + this.containerId);
this.places = places;
this.allImagesLoaded = false;
@@ -61,6 +64,82 @@ define([
this.waitForAllImagesToBeLoaded();
}
/**
* Change all the drags and drops related to the item that has been changed by filter to correct size and content.
*
* @param {object} filteredElement the element has been modified by filter.
*/
DragDropOntoImageQuestion.prototype.changeAllDragsAndDropsToFilteredContent = function(filteredElement) {
let currentFilteredItem = $(filteredElement);
const parentIsDD = currentFilteredItem.parent().closest('div').hasClass('placed') ||
currentFilteredItem.parent().hasClass('draghome');
const isDD = currentFilteredItem.hasClass('placed') || currentFilteredItem.hasClass('draghome');
// The filtered element or parent element should a drag or drop item.
if (!parentIsDD && !isDD) {
return;
}
if (parentIsDD) {
currentFilteredItem = currentFilteredItem.parent().closest('div');
}
if (this.getRoot().find(currentFilteredItem).length <= 0) {
// If the DD item doesn't belong to this question
// In case we have multiple questions in the same page.
return;
}
const group = this.getGroup(currentFilteredItem),
choice = this.getChoice(currentFilteredItem);
let listOfModifiedDragDrop = [];
// Get the list of drag and drop item within the same group and choice.
this.getRoot().find('.group' + group + '.choice' + choice).each(function(i, node) {
// Same modified item, skip it.
if ($(node).get(0) === currentFilteredItem.get(0)) {
return;
}
const originalClass = $(node).attr('class');
const originalStyle = $(node).attr('style');
// We want to keep all the handler and event for filtered item, so using clone is the only choice.
const filteredDragDropClone = currentFilteredItem.clone();
// Sometimes, for the question that has a lot of input groups and unlimited draggable items,
// this 'clone' process takes longer than usual,it will not add the eventHandler for this cloned drag.
// We need to make sure to add the eventHandler for the cloned drag too.
questionManager.addEventHandlersToDrag(filteredDragDropClone);
// Replace the class and style of the drag drop item we want to replace for the clone.
filteredDragDropClone.attr('class', originalClass);
filteredDragDropClone.attr('style', originalStyle);
// Insert into DOM.
$(node).before(filteredDragDropClone);
// Add the item has been replaced to a list so we can remove it later.
listOfModifiedDragDrop.push(node);
});
listOfModifiedDragDrop.forEach(function(node) {
$(node).remove();
});
// Save the current height and width.
const currentHeight = currentFilteredItem.height();
const currentWidth = currentFilteredItem.width();
// Set to auto, so we can get the real height and width of the filtered item.
currentFilteredItem.height('auto');
currentFilteredItem.width('auto');
// We need to set display block so we can get height and width.
// Some browsers can't get the offsetWidth/Height if they are an inline element like span tag.
if (!filteredElement.offsetWidth || !filteredElement.offsetHeight) {
filteredElement.classList.add('d-block');
}
if (this.questionDragDropWidthHeight[group].maxWidth < Math.ceil(filteredElement.offsetWidth) ||
this.questionDragDropWidthHeight[group].maxHeight < Math.ceil(0 + filteredElement.offsetHeight)) {
// Remove the d-block class before calculation.
filteredElement.classList.remove('d-block');
// Now resize all the items in the same group if we have new maximum width or height.
this.resizeAllDragsAndDropsInGroup(group);
} else {
currentFilteredItem.height(currentHeight);
currentFilteredItem.width(currentWidth);
}
// Remove the d-block class after resize.
filteredElement.classList.remove('d-block');
};
/**
* Waits until all images are loaded before calling setupQuestion().
*
@@ -94,6 +173,12 @@ define([
// We now have all images. Carry on, but only after giving the layout a chance to settle down.
this.allImagesLoaded = true;
thisQ.setupQuestion();
// Wait for all dynamic content loaded by filter to be completed.
document.addEventListener(filterEvent.eventTypes.filterContentRenderingComplete, (elements) => {
elements.detail.nodes.forEach((element) => {
thisQ.changeAllDragsAndDropsToFilteredContent(element);
});
});
};
/**
@@ -135,7 +220,7 @@ define([
var thisQ = this;
this.getRoot().find('.draghomes > div').each(function(i, node) {
thisQ.resizeAllDragsAndDropsInGroup(
thisQ.getClassnameNumericSuffix($(node), 'dragitemgroup'));
thisQ.getClassnameNumericSuffix($(node), 'dragitemgroup'));
});
};
@@ -146,7 +231,7 @@ define([
*/
DragDropOntoImageQuestion.prototype.resizeAllDragsAndDropsInGroup = function(group) {
var root = this.getRoot(),
dragHomes = root.find('.dragitemgroup' + group + ' .draghome'),
dragHomes = root.find(".draghome.group" + group),
maxWidth = 0,
maxHeight = 0;
@@ -159,18 +244,11 @@ define([
// The size we will want to set is a bit bigger than this.
maxWidth += 10;
maxHeight += 10;
this.questionDragDropWidthHeight[group] = {maxWidth, maxHeight};
// Set each drag home to that size.
dragHomes.each(function(i, drag) {
var left = Math.round((maxWidth - drag.offsetWidth) / 2),
top = Math.floor((maxHeight - drag.offsetHeight) / 2);
// Set top and left padding so the item is centred.
$(drag).css({
'padding-left': left + 'px',
'padding-right': (maxWidth - drag.offsetWidth - left) + 'px',
'padding-top': top + 'px',
'padding-bottom': (maxHeight - drag.offsetHeight - top) + 'px'
});
$(drag).width(maxWidth).height(maxHeight).css('lineHeight', maxHeight + 'px');
});
// Create the drops and make them the right size.
@@ -186,9 +264,11 @@ define([
if (label === '') {
label = M.util.get_string('blank', 'qtype_ddimageortext');
}
root.find('.dropzones').append('<div class="dropzone active group' + place.group +
' place' + i + '" tabindex="0">' +
if (root.find('.dropzones .dropzone.group' + place.group + '.place' + i).length === 0) {
root.find('.dropzones').append('<div class="dropzone active group' + place.group +
' place' + i + '" tabindex="0">' +
'<span class="accesshide">' + label + '</span>&nbsp;</div>');
}
root.find('.dropzone.place' + i).width(maxWidth - 2).height(maxHeight - 2);
}
};
+1 -1
View File
@@ -52,7 +52,6 @@ class qtype_ddtoimage_renderer_base extends qtype_with_combined_feedback_rendere
public function formulation_and_controls(question_attempt $qa,
question_display_options $options) {
$question = $qa->get_question();
$response = $qa->get_last_qt_data();
@@ -92,6 +91,7 @@ class qtype_ddtoimage_renderer_base extends qtype_with_combined_feedback_rendere
$classes[] = 'infinite';
}
if ($dragimageurl === null) {
$dragimage->text = question_utils::format_question_fragment($dragimage->text, $this->page->context);
$dragimagehomesgroup .= html_writer::div($dragimage->text, join(' ', $classes), ['src' => $dragimageurl]);
} else {
$dragimagehomesgroup .= html_writer::img($dragimageurl, $dragimage->text, ['class' => join(' ', $classes)]);
+4
View File
@@ -85,6 +85,10 @@ form.mform fieldset#id_previewareaheader .droppreview {
display: none;
}
.que.ddimageortext .MathJax_Display {
margin: 0;
}
.que.ddimageortext .draghomes .draghome.dragplaceholder.active {
visibility: hidden;
display: inline-block;
@@ -18,8 +18,9 @@ Feature: Preview a drag-drop onto image question
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | template |
| Test questions | ddimageortext | Drag onto image | xsection |
| questioncategory | qtype | name | template |
| Test questions | ddimageortext | Drag onto image | xsection |
| Test questions | ddimageortext | Drag to mathjax equation | mathjax |
@javascript @_bug_phantomjs
Scenario: Preview a question using the mouse.
@@ -53,3 +54,12 @@ Feature: Preview a drag-drop onto image question
And I press "Submit and finish"
Then the state of "Identify the features" question is shown as "Correct"
And I should see "Mark 1.00 out of 1.00"
@javascript
Scenario: Preview a drag-drop into image question with mathjax question.
Given the "mathjaxloader" filter is "on"
And the "mathjaxloader" filter applies to "content and headings"
And I am on the "Drag to mathjax equation" "core_question > preview" page logged in as teacher
And I press "Fill in correct responses"
When I press "Submit and finish"
Then ".filter_mathjaxloader_equation" "css_element" should exist in the ".draghome" "css_element"
+49 -1
View File
@@ -34,7 +34,7 @@ defined('MOODLE_INTERNAL') || die();
*/
class qtype_ddimageortext_test_helper extends question_test_helper {
public function get_test_questions() {
return array('fox', 'maths', 'xsection', 'mixedlang');
return ['fox', 'maths', 'xsection', 'mixedlang', 'mathjax'];
}
/**
@@ -251,6 +251,54 @@ class qtype_ddimageortext_test_helper extends question_test_helper {
return $fromform;
}
/**
* Get data required to save a drag-drop into text question where the the answer contain equation
*
*
* @return stdClass data to create a ddwtos question.
*/
public function get_ddimageortext_question_form_data_mathjax() {
global $CFG, $USER;
$fromform = new stdClass();
$bgdraftitemid = 0;
file_prepare_draft_area($bgdraftitemid, null, null, null, null);
$fs = get_file_storage();
$filerecord = new stdClass();
$filerecord->contextid = context_user::instance($USER->id)->id;
$filerecord->component = 'user';
$filerecord->filearea = 'draft';
$filerecord->itemid = $bgdraftitemid;
$filerecord->filepath = '/';
$filerecord->filename = 'oceanfloorbase.jpg';
$fs->create_file_from_pathname($filerecord, $CFG->dirroot .
'/question/type/ddimageortext/tests/fixtures/oceanfloorbase.jpg');
$fromform->name = 'Drag-and-drop words into image question with equation';
$fromform->questiontext = ['text' => 'Fill in the correct mathjax equation: y = 2, x =4', 'format' => FORMAT_HTML];
$fromform->defaultmark = 1.0;
$fromform->generalfeedback = ['text' => 'The right answer is: "y = x^2"', 'format' => FORMAT_HTML];
$fromform->drags = [
['dragitemtype' => 'word', 'draggroup' => '1', 'infinite' => '0'],
['dragitemtype' => 'word', 'draggroup' => '1', 'infinite' => '0'],
];
$fromform->bgimage = $bgdraftitemid;
$fromform->dragitem = [0, 0];
$fromform->draglabel =
[
'$$ y = x^2 $$',
'$$ y = x^5 $$',
];
$fromform->drops = [
['xleft' => '53', 'ytop' => '17', 'choice' => '1', 'droplabel' => ''],
['xleft' => '172', 'ytop' => '2', 'choice' => '2', 'droplabel' => ''],
];
test_question_maker::set_standard_combined_feedback_form_data($fromform);
$fromform->shownumcorrect = 0;
$fromform->penalty = 0.3333333;
$fromform->status = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY;
return $fromform;
}
/**
* Make a test question where the drag items are a different language than the main question text.
*