Merge pull request #4176 from dpalou/MOBILE-4587
MOBILE-4587 qtype: Fix race condition with MathJax in D&D questionsmain
commit
437f309957
|
@ -177,7 +177,7 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.waitForReady();
|
await this.waitForReady();
|
||||||
|
|
||||||
this.window.M!.filter_mathjaxloader!.typeset(container);
|
await this.window.M!.filter_mathjaxloader!.typeset(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -234,24 +234,32 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Called by the filter when an equation is found while rendering the page.
|
// Called by the filter when an equation is found while rendering the page.
|
||||||
typeset: function (container: HTMLElement): void {
|
typeset: async function (container: HTMLElement): Promise<void> {
|
||||||
if (!this._configured) {
|
if (!this._configured) {
|
||||||
this._setLocale();
|
this._setLocale();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (that.window.MathJax !== undefined) {
|
if (that.window.MathJax === undefined) {
|
||||||
const processDelay = that.window.MathJax.Hub.processSectionDelay;
|
return;
|
||||||
// Set the process section delay to 0 when updating the formula.
|
|
||||||
that.window.MathJax.Hub.processSectionDelay = 0;
|
|
||||||
|
|
||||||
const equations = Array.from(container.querySelectorAll('.filter_mathjaxloader_equation'));
|
|
||||||
equations.forEach((node) => {
|
|
||||||
that.window.MathJax.Hub.Queue(['Typeset', that.window.MathJax.Hub, node], [that.fixUseUrls, node]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set the delay back to normal after processing.
|
|
||||||
that.window.MathJax.Hub.processSectionDelay = processDelay;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const processDelay = that.window.MathJax.Hub.processSectionDelay;
|
||||||
|
// Set the process section delay to 0 when updating the formula.
|
||||||
|
that.window.MathJax.Hub.processSectionDelay = 0;
|
||||||
|
|
||||||
|
const equations = Array.from(container.querySelectorAll('.filter_mathjaxloader_equation'));
|
||||||
|
const promises = equations.map((node) => new Promise<void>((resolve) => {
|
||||||
|
that.window.MathJax.Hub.Queue(
|
||||||
|
['Typeset', that.window.MathJax.Hub, node],
|
||||||
|
[that.fixUseUrls, node],
|
||||||
|
[resolve],
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Set the delay back to normal after processing.
|
||||||
|
that.window.MathJax.Hub.processSectionDelay = processDelay;
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,6 @@
|
||||||
<div class="fake-ion-item ion-text-wrap" [class.readonly]="question.readOnly" [hidden]="!question.loaded">
|
<div class="fake-ion-item ion-text-wrap" [class.readonly]="question.readOnly" [hidden]="!question.loaded">
|
||||||
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId"
|
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId"
|
||||||
[text]="question.ddArea" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
[text]="question.ddArea" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
||||||
(afterRender)="ddAreaRendered()" />
|
(filterContentRenderingComplete)="ddAreaRendered()" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,6 @@
|
||||||
<div class="fake-ion-item ion-text-wrap" [hidden]="!question.loaded">
|
<div class="fake-ion-item ion-text-wrap" [hidden]="!question.loaded">
|
||||||
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId"
|
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId"
|
||||||
[text]="question.ddArea" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
[text]="question.ddArea" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
||||||
(afterRender)="ddAreaRendered()" />
|
(filterContentRenderingComplete)="ddAreaRendered()" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { CoreFormatTextDirective } from '@directives/format-text';
|
import { CoreFormatTextDirective } from '@directives/format-text';
|
||||||
import { CoreText } from '@singletons/text';
|
|
||||||
import { CoreDirectivesRegistry } from '@singletons/directives-registry';
|
import { CoreDirectivesRegistry } from '@singletons/directives-registry';
|
||||||
import { CoreCoordinates, CoreDom } from '@singletons/dom';
|
import { CoreCoordinates, CoreDom } from '@singletons/dom';
|
||||||
import { CoreEventObserver } from '@singletons/events';
|
import { CoreEventObserver } from '@singletons/events';
|
||||||
|
@ -489,10 +488,6 @@ export class AddonQtypeDdwtosQuestion {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
groupItems.forEach((item) => {
|
|
||||||
item.innerHTML = CoreText.decodeHTML(item.innerHTML);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait to render in order to calculate size.
|
// Wait to render in order to calculate size.
|
||||||
if (groupItems[0].parentElement) {
|
if (groupItems[0].parentElement) {
|
||||||
// Wait for parent to be visible. We cannot wait for group items because they have visibility hidden.
|
// Wait for parent to be visible. We cannot wait for group items because they have visibility hidden.
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
<core-format-text *ngIf="question.answers" [component]="component" [componentId]="componentId" [text]="question.answers"
|
<core-format-text *ngIf="question.answers" [component]="component" [componentId]="componentId" [text]="question.answers"
|
||||||
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
||||||
(afterRender)="answersRendered()" />
|
(filterContentRenderingComplete)="answersRendered()" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { AddonModQuizQuestionBasicData, CoreQuestionBaseComponent } from '@featu
|
||||||
import { CoreQuestionHelper } from '@features/question/services/question-helper';
|
import { CoreQuestionHelper } from '@features/question/services/question-helper';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { AddonQtypeDdwtosQuestion } from '../classes/ddwtos';
|
import { AddonQtypeDdwtosQuestion } from '../classes/ddwtos';
|
||||||
|
import { CoreText } from '@singletons/text';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component to render a drag-and-drop words into sentences question.
|
* Component to render a drag-and-drop words into sentences question.
|
||||||
|
@ -69,6 +70,13 @@ export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent<AddonMo
|
||||||
}
|
}
|
||||||
|
|
||||||
this.question.readOnly = answerContainer.classList.contains('readonly');
|
this.question.readOnly = answerContainer.classList.contains('readonly');
|
||||||
|
|
||||||
|
// Decode content of drag homes. This must be done before filters are applied, otherwise some things don't work as expected.
|
||||||
|
const groupItems = Array.from(answerContainer.querySelectorAll<HTMLElement>('span.draghome'));
|
||||||
|
groupItems.forEach((item) => {
|
||||||
|
item.innerHTML = CoreText.decodeHTML(item.innerHTML);
|
||||||
|
});
|
||||||
|
|
||||||
// Add the drags container inside the answers so it's rendered inside core-format-text,
|
// Add the drags container inside the answers so it's rendered inside core-format-text,
|
||||||
// otherwise some styles could be different between the drag homes and the draggables.
|
// otherwise some styles could be different between the drag homes and the draggables.
|
||||||
this.question.answers = answerContainer.outerHTML + '<div class="drags"></div>';
|
this.question.answers = answerContainer.outerHTML + '<div class="drags"></div>';
|
||||||
|
|
|
@ -94,7 +94,8 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
|
||||||
@Input({ transform: toBoolean }) hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
|
@Input({ transform: toBoolean }) hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
|
||||||
@Input({ transform: toBoolean }) disabled = false; // If disabled, autoplay elements will be disabled.
|
@Input({ transform: toBoolean }) disabled = false; // If disabled, autoplay elements will be disabled.
|
||||||
|
|
||||||
@Output() afterRender: EventEmitter<void>; // Called when the data is rendered.
|
@Output() afterRender = new EventEmitter<void>(); // Called when the data is rendered.
|
||||||
|
@Output() filterContentRenderingComplete = new EventEmitter<void>(); // Called when the filters have finished rendering content.
|
||||||
@Output() onClick: EventEmitter<void> = new EventEmitter(); // Called when clicked.
|
@Output() onClick: EventEmitter<void> = new EventEmitter(); // Called when clicked.
|
||||||
|
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
|
@ -117,8 +118,6 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
|
||||||
this.emptyText = this.hideIfEmpty ? '' : ' ';
|
this.emptyText = this.hideIfEmpty ? '' : ' ';
|
||||||
this.element.innerHTML = this.emptyText;
|
this.element.innerHTML = this.emptyText;
|
||||||
|
|
||||||
this.afterRender = new EventEmitter<void>();
|
|
||||||
|
|
||||||
this.element.addEventListener('click', (event) => this.elementClicked(event));
|
this.element.addEventListener('click', (event) => this.elementClicked(event));
|
||||||
|
|
||||||
this.siteId = this.siteId || CoreSites.getCurrentSiteId();
|
this.siteId = this.siteId || CoreSites.getCurrentSiteId();
|
||||||
|
@ -340,8 +339,10 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish the rendering, displaying the element again and calling afterRender.
|
* Finish the rendering, displaying the element again and calling afterRender.
|
||||||
|
*
|
||||||
|
* @param triggerFilterRender Whether to emit the filterContentRenderingComplete output too.
|
||||||
*/
|
*/
|
||||||
protected async finishRender(): Promise<void> {
|
protected async finishRender(triggerFilterRender = true): Promise<void> {
|
||||||
// Show the element again.
|
// Show the element again.
|
||||||
this.element.classList.remove('core-loading');
|
this.element.classList.remove('core-loading');
|
||||||
|
|
||||||
|
@ -349,6 +350,9 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
|
||||||
|
|
||||||
// Emit the afterRender output.
|
// Emit the afterRender output.
|
||||||
this.afterRender.emit();
|
this.afterRender.emit();
|
||||||
|
if (triggerFilterRender) {
|
||||||
|
this.filterContentRenderingComplete.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -402,11 +406,13 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
|
||||||
this.component,
|
this.component,
|
||||||
this.componentId,
|
this.componentId,
|
||||||
result.siteId,
|
result.siteId,
|
||||||
);
|
).finally(() => {
|
||||||
|
this.filterContentRenderingComplete.emit();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.element.classList.remove('core-disable-media-adapt');
|
this.element.classList.remove('core-disable-media-adapt');
|
||||||
await this.finishRender();
|
await this.finishRender(!result.options.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue