This commit is contained in:
Sara Arjona
2023-01-02 13:07:34 +01:00
4 changed files with 140 additions and 20 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+88 -18
View File
@@ -57,9 +57,11 @@ define([
if (readOnly) {
this.getRoot().addClass('qtype_ddmarker-readonly');
}
thisQ.cloneDrags();
thisQ.repositionDrags();
thisQ.drawDropzones();
thisQ.allImagesLoaded = false;
thisQ.getNotYetLoadedImages().one('load', function() {
thisQ.waitForAllImagesToBeLoaded();
});
thisQ.waitForAllImagesToBeLoaded();
}
/**
@@ -147,13 +149,17 @@ define([
root.find('input.choices').each(function(key, input) {
var choiceNo = thisQ.getChoiceNoFromElement(input),
coords = thisQ.getCoords(input);
if (coords.length) {
imageCoords = thisQ.getImageCoords(input);
if (imageCoords.length) {
var drag = thisQ.getRoot().find('.draghomes' + ' span.marker' + '.choice' + choiceNo).not('.dragplaceholder');
drag.remove();
for (var i = 0; i < coords.length; i++) {
for (var i = 0; i < imageCoords.length; i++) {
var dragInDrop = drag.clone();
dragInDrop.data('pagex', coords[i].x).data('pagey', coords[i].y);
// Convert image coords to screen coords.
const screenCoords = thisQ.convertToWindowXY(imageCoords[i]);
dragInDrop.data('pagex', screenCoords.x).data('pagey', screenCoords.y);
// Save image coords to the drag item so we can use it later.
dragInDrop.data('imageCoords', imageCoords[i]);
// We always save the coordinates in the 1:1 ratio.
// So we need to set the scale ratio to 1 for the initial load.
dragInDrop.data('scaleRatio', 1);
@@ -214,18 +220,18 @@ define([
* drags should be shown.
*
* @param {jQuery} inputNode
* @returns {Point[]} coordinates of however many copies of the drag item should be shown.
* @returns {Point[]} image coordinates of however many copies of the drag item should be shown.
*/
DragDropMarkersQuestion.prototype.getCoords = function(inputNode) {
var coords = [],
DragDropMarkersQuestion.prototype.getImageCoords = function(inputNode) {
var imageCoords = [],
val = $(inputNode).val();
if (val !== '') {
var coordsStrings = val.split(';');
for (var i = 0; i < coordsStrings.length; i++) {
coords[i] = this.convertToWindowXY(Shapes.Point.parse(coordsStrings[i]));
imageCoords[i] = Shapes.Point.parse(coordsStrings[i]);
}
}
return coords;
return imageCoords;
};
/**
@@ -328,7 +334,12 @@ define([
if (this.coordsInBgImg(dragXY)) {
this.sendDragToDrop(dragged, true);
placed = true;
// Since we already move the drag item to new position.
// Remove the image coords if this drag item have it.
// We will get the new image coords for this drag item in saveCoordsForChoice.
if (dragged.data('imageCoords')) {
dragged.data('imageCoords', null);
}
// It seems that the dragdrop sometimes leaves the drag
// one pixel out of position. Put it in exactly the right place.
var bgImgXY = this.convertToBgImgXY(dragXY);
@@ -351,15 +362,15 @@ define([
* @param {Number} choiceNo which copy of the choice this was.
*/
DragDropMarkersQuestion.prototype.saveCoordsForChoice = function(choiceNo) {
var coords = [],
items = this.getRoot().find('div.droparea span.marker.choice' + choiceNo),
let imageCoords = [];
var items = this.getRoot().find('div.droparea span.marker.choice' + choiceNo),
thiQ = this,
bgRatio = this.bgRatio();
if (items.length) {
items.each(function() {
var drag = $(this);
if (!drag.hasClass('beingdragged')) {
if (!drag.hasClass('beingdragged') && !drag.data('imageCoords')) {
if (drag.data('scaleRatio') !== bgRatio) {
// The scale ratio for the draggable item was changed. We need to update that.
drag.data('pagex', drag.offset().left).data('pagey', drag.offset().top);
@@ -368,13 +379,15 @@ define([
if (thiQ.coordsInBgImg(dragXY)) {
var bgImgXY = thiQ.convertToBgImgXY(dragXY);
bgImgXY = new Shapes.Point(bgImgXY.x / bgRatio, bgImgXY.y / bgRatio);
coords[coords.length] = bgImgXY;
imageCoords[imageCoords.length] = bgImgXY;
}
} else if (drag.data('imageCoords')) {
imageCoords[imageCoords.length] = drag.data('imageCoords');
}
});
}
this.getRoot().find('input.choice' + choiceNo).val(coords.join(';'));
this.getRoot().find('input.choice' + choiceNo).val(imageCoords.join(';'));
if (this.isQuestionInteracted()) {
// The user has interacted with the draggable items. We need to mark the form as dirty.
questionManager.handleFormDirty();
@@ -737,6 +750,63 @@ define([
return drag.hasClass('infinite');
};
/**
* Waits until all images are loaded before calling setupQuestion().
*
* This function is called from the onLoad of each image, and also polls with
* a time-out, because image on-loads are allegedly unreliable.
*/
DragDropMarkersQuestion.prototype.waitForAllImagesToBeLoaded = function() {
// This method may get called multiple times (via image on-loads or timeouts.
// If we are already done, don't do it again.
if (this.allImagesLoaded) {
return;
}
// Clear any current timeout, if set.
if (this.imageLoadingTimeoutId !== null) {
clearTimeout(this.imageLoadingTimeoutId);
}
// If we have not yet loaded all images, set a timeout to
// call ourselves again, since apparently images on-load
// events are flakey.
if (this.getNotYetLoadedImages().length > 0) {
this.imageLoadingTimeoutId = setTimeout(function() {
this.waitForAllImagesToBeLoaded();
}, 100);
return;
}
// We now have all images. Carry on, but only after giving the layout a chance to settle down.
this.allImagesLoaded = true;
this.cloneDrags();
this.repositionDrags();
this.drawDropzones();
};
/**
* Get any of the images in the drag-drop area that are not yet fully loaded.
*
* @returns {jQuery} those images.
*/
DragDropMarkersQuestion.prototype.getNotYetLoadedImages = function() {
return this.getRoot().find('.ddmarker img.dropbackground').not(function(i, imgNode) {
return this.imageIsLoaded(imgNode);
});
};
/**
* Check if an image has loaded without errors.
*
* @param {HTMLImageElement} imgElement an image.
* @returns {boolean} true if this image has loaded without errors.
*/
DragDropMarkersQuestion.prototype.imageIsLoaded = function(imgElement) {
return imgElement.complete && imgElement.naturalHeight !== 0;
};
/**
* Singleton that tracks all the DragDropToTextQuestions on this page, and deals
* with event dispatching.
@@ -0,0 +1,50 @@
@qtype @qtype_ddmarker
Feature: Preview a quiz with multiple maker question.
As a teacher
In order to check my drag-drop marker questions will work for students
I need to preview them in quiz with multiple questions.
Background:
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "question categories" exist:
| contextlevel | reference | name |
| Course | C1 | Test questions |
And the following "questions" exist:
| questioncategory | qtype | name | template |
| Test questions | ddmarker | Drag markers | mkmap |
| Test questions | ddmarker | Drag markers 2 | mkmap |
And the following "activities" exist:
| activity | name | course | idnumber |
| quiz | Test quiz | C1 | quiz1 |
And quiz "Test quiz" contains the following questions:
| Drag markers | 1 |
| Drag markers 2 | 2 |
@javascript
Scenario: Preview a quiz with multiple markers question
Given I am on the "Test quiz" "mod_quiz > View" page logged in as "admin"
And I press "Preview quiz"
# Add change window size so we can drag-drop OU marker on 322,213 coordinates on firefox.
And I change viewport size to "large"
# Drag items and go back and forth between the question.
And I drag "OU" to "322,213" in the drag and drop markers question
And I drag "Railway station" to "144,84" in the drag and drop markers question
And I drag "Railway station" to "195,180" in the drag and drop markers question
And I press "Next page"
And I drag "OU" to "322,213" in the drag and drop markers question
And I drag "Railway station" to "144,84" in the drag and drop markers question
And I drag "Railway station" to "195,180" in the drag and drop markers question
And I press "Previous page"
And I drag "Railway station" to "267,302" in the drag and drop markers question
And I press "Next page"
And I drag "Railway station" to "267,302" in the drag and drop markers question
And I press "Previous page"
And I press "Next page"
And I press "Finish attempt ..."
And I press "Submit all and finish"
When I click on "Submit all and finish" "button" in the "Submit all your answers and finish?" "dialogue"
Then I should see "2.00/2.00"
And the state of "Please place the markers on the map of Milton Keynes and be aware that" question is shown as "Correct"
And I should see "Well done!"