Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HFP-3041 HFP-2919 Fix LaTeX support in Drag the Words #89

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 128 additions & 13 deletions src/scripts/drag-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {

//Special Sub-containers:
var DRAGGABLES_WIDE_SCREEN = 'h5p-drag-wide-screen';
var DRAGGABLE_ELEMENT_WIDE_SCREEN = 'h5p-drag-draggable-wide-screen';
var DRAGGABLE_ELEMENT_WIDE_SCREEN = 'wide-screen';

/**
* Initialize module.
Expand Down Expand Up @@ -116,6 +116,9 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {
// Keeps track of if Question has been answered
this.answered = false;

// True if some solution text contains LaTeX
this.containsLatex = false,

// Convert line breaks to HTML
this.textFieldHtml = this.params.textField.replace(/(\r\n|\n|\r)/gm, "<br/>");

Expand Down Expand Up @@ -397,9 +400,92 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {
* Changes layout responsively when resized.
*/
DragText.prototype.resize = function () {
self = this;
this.changeLayoutToFitWidth();

if (!this.containsLatex) {
return; // No further processing needed.
}

// Reset width and height to determine natural size
this.draggables.forEach(function (draggable) {
draggable.getDraggableElement().css('height', '');
draggable.getDraggableElement().css('width', '');
});

// Compute new maximum sizes of draggables
this.maxSizes = this.getMaxDraggableSizes(this.maxSizes);

// Set draggables' size
this.draggables.forEach(function (draggable) {
const $draggableElement = draggable.getDraggableElement();
if ($draggableElement.hasClass('wide-screen') || $draggableElement.hasClass('h5p-drag-dropped')) {
$draggableElement.css('width', `${self.maxSizes.width}px`);
}
else {
$draggableElement.css('width', '');
}

$draggableElement.css('height', `${self.maxSizes.height}px`);
});

// Set droppables' size
this.droppables.forEach(function (droppable) {
droppable.getElement().style.width = `${self.maxSizes.width}px`;
droppable.getElement().style.height = `${self.maxSizes.height}px`;

droppable.$showSolution.css({width: ''});
droppable.$showSolution.css({
height: `${self.maxSizes.height}px`,
width: droppable.$showSolution.width() // Workaround for MathJax re-rendering, avoid reflowing of text, still flickers
});
});

this.$wordContainer.css('line-height', `${1.5 * self.maxSizes.height}px`);
};

/**
* Get maximum sizes (width, outerWidth, height, outerHeight) from draggables.
* @param {object} initial Initial values.
* @param {number} [initial.width=0] Initial width.
* @param {number} [initial.height=0] Initial height.
* @return {object} Maximum sizes from draggables.
*/
DragText.prototype.getMaxDraggableSizes = function (initial = {width: 0, height: 0}) {
const undoneDraggables = this.draggables.filter(function (draggable) {
// Ignore draggables that have been dropped
return !draggable.getDraggableElement().hasClass('h5p-drag-dropped');
});

// Required for scaling as child of content types that adjust font size
const currentFontSize = parseInt(this.$inner.css('font-size'), 10);
if (this.fontSize !== currentFontSize) {
this.fontSize = currentFontSize;
if (undoneDraggables.length > 0) {
initial = {width: 0, height: 0};
}
}

initial = undoneDraggables
.reduce(function (maxSizes, draggable) {
const element = draggable.getDraggableElement();
return {
width: Math.max(maxSizes.width, element.width()),
height: initial.height
};
}, initial);

// Get highest values for height and width
return this.draggables
.reduce(function (maxSizes, draggable) {
const element = draggable.getDraggableElement();
return {
width: initial.width,
height: Math.max(maxSizes.height, element.height())
};
}, initial);
}

/**
* Adds the draggables on the right side of the screen if widescreen is detected.
*/
Expand Down Expand Up @@ -473,7 +559,10 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {
//Show Solution button
self.addButton('show-solution', self.params.showSolution, function () {
self.droppables.forEach(function (droppable) {
droppable.showSolution();
const matchingDraggable = self.draggables.filter(function (draggable) {
return draggable.getAnswerText() === droppable.text;
}).shift();
droppable.showSolution(matchingDraggable);
});
self.draggables.forEach(draggable => self.setDraggableAriaLabel(draggable));
self.disableDraggables();
Expand Down Expand Up @@ -791,9 +880,26 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {
.forEach(function(part) {
if(self.isAnswerPart(part)) {
// is draggable/droppable
const solution = lex(part);
self.createDraggable(solution.text);
self.createDroppable(solution.text, solution.tip, solution.correctFeedback, solution.incorrectFeedback);
const solution = lex(part)

// Only inline LaTeX allowed
solution.text = solution.text
.replace(/\\\[(.*?)\\\]/g, '\\\($1\\\)')
.replace(/\$\$(.*?)\$\$/g, '\\\($1\\\)');

// Keep track of whether answers contain LaTeX
self.containsLatex = self.containsLatex || /\\\(.*?\\\)/.test(solution.text);

const draggable = self.createDraggable(solution.text);
const droppable = self.createDroppable(solution.text, solution.tip, solution.correctFeedback, solution.incorrectFeedback);

// trigger instant feedback
if (self.params.behaviour.instantFeedback) {
draggable.getDraggableElement().on('dragstop', function() {
droppable.addFeedback();
self.instantFeedbackEvaluation();
});
}
}
else {
// is normal text
Expand All @@ -804,6 +910,7 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {

self.shuffleAndAddDraggables(self.$draggables);
self.$draggables.appendTo(self.$taskContainer);
self.$wordContainer.toggleClass('h5p-drag-text-has-latex', self.containsLatex);
self.$wordContainer.appendTo(self.$taskContainer);
self.$taskContainer.appendTo($container);
self.addDropzoneWidth();
Expand Down Expand Up @@ -835,14 +942,14 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {
this.draggables.forEach(function (draggable) {
var $draggableElement = draggable.getDraggableElement();

const cssOptions = (draggable.containsLatex) ?
{'width': 'auto'} :
{'position': 'absolute', 'white-space': 'nowrap', 'width': 'auto', 'padding': 0, 'margin': 0}

//Find the initial natural width of the draggable.
var $tmp = $draggableElement.clone().css({
'position': 'absolute',
'white-space': 'nowrap',
'width': 'auto',
'padding': 0,
'margin': 0
}).html(draggable.getAnswerText())
var $tmp = $draggableElement.clone()
.css(cssOptions)
.html(draggable.getAnswerText())
.appendTo($draggableElement.parent());
var width = $tmp.outerWidth();

Expand Down Expand Up @@ -883,6 +990,7 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {

//Make the draggable
var $draggable = $('<div/>', {
class: 'h5p-drag-draggable',
html: `<span>${answer}</span>`,
role: 'button',
'aria-grabbed': 'false',
Expand Down Expand Up @@ -1262,10 +1370,17 @@ H5P.DragText = (function ($, Question, ConfirmationDialog) {
* Sets feedback on the dropzones.
*/
DragText.prototype.showSolutions = function () {
const self = this;

this.showEvaluation(true);
this.droppables.forEach(function (droppable) {
droppable.addFeedback();
droppable.showSolution();

const matchingDraggable = self.draggables.filter(function (draggable) {
return draggable.getAnswerText() === droppable.text;
}).shift();

droppable.showSolution(matchingDraggable);
});

this.removeAllDroppablesFromControls();
Expand Down
13 changes: 12 additions & 1 deletion src/scripts/draggable.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ H5P.TextDraggable = (function ($) {
self.index = index;
self.initialIndex = index;

// Shortening LaTeX would destroy it
this.containsLatex = self.shortFormat.match(/\\\(.+\\\)|\\\[.+\\\]|\$\$.+\$\$/);

self.shortFormat = self.text;
//Shortens the draggable string if inside a dropbox.
if (self.shortFormat.length > 20 && !self.shortFormat.match(/\\\(.+\\\)|\\\[.+\\\]|\$\$.+\$\$/)) {
if (!this.containsLatex && self.shortFormat.length > 20) {
self.shortFormat = self.shortFormat.slice(0, 17) + '...';
}

Expand Down Expand Up @@ -212,6 +215,10 @@ H5P.TextDraggable = (function ($) {
* Sets short format of draggable when inside a dropbox.
*/
Draggable.prototype.setShortFormat = function () {
if (this.containsLatex) {
return; // Prevent unnecessary re-rerendering
}

this.$draggable.html(this.shortFormat);
};

Expand All @@ -228,6 +235,10 @@ H5P.TextDraggable = (function ($) {
* Removes the short format of draggable when it is outside a dropbox.
*/
Draggable.prototype.removeShortFormat = function () {
if (this.containsLatex) {
return; // Prevent unnecessary re-rerendering
}

this.$draggable.html(this.text);
};

Expand Down
24 changes: 22 additions & 2 deletions src/scripts/droppable.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,35 @@ H5P.TextDroppable = (function ($) {
/**
* Displays the solution next to the drop box if it is not correct.
*/
Droppable.prototype.showSolution = function () {
Droppable.prototype.showSolution = function (correctDraggable) {
// Reset fixed width
this.$showSolution.css('width', '');

const correct = (this.containedDraggable !== null) && (this.containedDraggable.getAnswerText() === this.text);
if (!correct) {
this.$showSolution.html(this.text);
if (correctDraggable.containsLatex) {
/*
* Use LaTeX already rendered for Draggable. MathJax still re-renders,
* but way quicker than if adding LaTaX as text. Should probably be tackled
* in MathDisplay?
* https://github.com/h5p/h5p-math-display/blob/5c2e312822c71f6d8bd33d3260f3ea92b697942e/scripts/mathdisplay.js#L232-L234
*/
this.$showSolution.html(correctDraggable.getDraggableElement().first().html());
this.$showSolution.addClass('h5p-drag-text-latex');
}
else {
this.$showSolution.html(this.text);
}
}

this.$showSolution.prepend(correct ? this.$correctText : this.$incorrectText);
this.$showSolution.toggleClass('incorrect', !correct);
this.$showSolution.show();

// Workaround for MathJax re-rendering, avoid reflowing of text, still flickers
this.$showSolution.css({
width: this.$showSolution.width()
});
};

/**
Expand Down
36 changes: 31 additions & 5 deletions src/styles/drag-text.css
Original file line number Diff line number Diff line change
Expand Up @@ -164,25 +164,38 @@
.h5p-drag-text .h5p-drag-dropped.h5p-drag-draggable-correct {
padding: 0;
color: #255c41;
border: none;
box-shadow: none;
line-height: 1.5;
background: none;
}

.h5p-drag-text .h5p-drag-dropped.h5p-drag-draggable-wrong {
padding: 0;
border: none;
color: #b71c1c;
box-shadow: none;
line-height: 1.5;
background: none;
}

.h5p-drag-text .h5p-drag-droppable-words:not(.h5p-drag-text-has-latex) .h5p-drag-dropped.h5p-drag-draggable-correct,
.h5p-drag-text .h5p-drag-droppable-words:not(.h5p-drag-text-has-latex) .h5p-drag-dropped.h5p-drag-draggable-wrong {
border: none;
}

.h5p-drag-text .h5p-drag-droppable-words.h5p-drag-text-has-latex .h5p-drag-dropped.h5p-drag-draggable-correct,
.h5p-drag-text .h5p-drag-droppable-words.h5p-drag-text-has-latex .h5p-drag-dropped.h5p-drag-draggable-wrong {
border-color: transparent;
}

/* Show solution container */
.h5p-drag-text .h5p-drag-show-solution-container {
position: relative;
display: inline;
display: inline-flex;
flex-direction: column;
height: 1.25em;
justify-content: center;
vertical-align: middle;
top: -0.1em;
}

.h5p-drag-text .h5p-drag-show-solution-container.incorrect {
Expand All @@ -195,6 +208,10 @@
margin-left: 0.5em;
}

.h5p-drag-text .h5p-drag-show-solution-container.h5p-drag-text-latex > span {
padding-top: 0.5em;
}

.h5p-drag-text .h5p-drag-droppable-words {
line-height: 1.75;
}
Expand All @@ -215,8 +232,17 @@
padding-top: 0;
}

.h5p-drag-text .h5p-drag-draggable-wide-screen {
display: block;
.h5p-drag-text .h5p-drag-draggable {
flex-direction: column;
justify-content: center;
}

.h5p-drag-text .h5p-drag-draggable:not(.wide-screen) {
display: inline-flex;
}

.h5p-drag-text .h5p-drag-draggable.wide-screen {
display: flex;
}

.h5p-drag-text [aria-dropeffect].ui-droppable.ui-droppable-disabled.ui-state-disabled{
Expand Down