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> {
|
||||
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.
|
||||
typeset: function (container: HTMLElement): void {
|
||||
typeset: async function (container: HTMLElement): Promise<void> {
|
||||
if (!this._configured) {
|
||||
this._setLocale();
|
||||
}
|
||||
|
||||
if (that.window.MathJax !== undefined) {
|
||||
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'));
|
||||
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;
|
||||
if (that.window.MathJax === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
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">
|
||||
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId"
|
||||
[text]="question.ddArea" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
||||
(afterRender)="ddAreaRendered()" />
|
||||
(filterContentRenderingComplete)="ddAreaRendered()" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -18,6 +18,6 @@
|
|||
<div class="fake-ion-item ion-text-wrap" [hidden]="!question.loaded">
|
||||
<core-format-text *ngIf="question.ddArea" [adaptImg]="false" [component]="component" [componentId]="componentId"
|
||||
[text]="question.ddArea" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
||||
(afterRender)="ddAreaRendered()" />
|
||||
(filterContentRenderingComplete)="ddAreaRendered()" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { CoreFormatTextDirective } from '@directives/format-text';
|
||||
import { CoreText } from '@singletons/text';
|
||||
import { CoreDirectivesRegistry } from '@singletons/directives-registry';
|
||||
import { CoreCoordinates, CoreDom } from '@singletons/dom';
|
||||
import { CoreEventObserver } from '@singletons/events';
|
||||
|
@ -489,10 +488,6 @@ export class AddonQtypeDdwtosQuestion {
|
|||
return;
|
||||
}
|
||||
|
||||
groupItems.forEach((item) => {
|
||||
item.innerHTML = CoreText.decodeHTML(item.innerHTML);
|
||||
});
|
||||
|
||||
// Wait to render in order to calculate size.
|
||||
if (groupItems[0].parentElement) {
|
||||
// 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"
|
||||
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="courseId"
|
||||
(afterRender)="answersRendered()" />
|
||||
(filterContentRenderingComplete)="answersRendered()" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -18,6 +18,7 @@ import { AddonModQuizQuestionBasicData, CoreQuestionBaseComponent } from '@featu
|
|||
import { CoreQuestionHelper } from '@features/question/services/question-helper';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { AddonQtypeDdwtosQuestion } from '../classes/ddwtos';
|
||||
import { CoreText } from '@singletons/text';
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
// 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,
|
||||
// otherwise some styles could be different between the drag homes and the draggables.
|
||||
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 }) 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.
|
||||
|
||||
protected element: HTMLElement;
|
||||
|
@ -117,8 +118,6 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
|
|||
this.emptyText = this.hideIfEmpty ? '' : ' ';
|
||||
this.element.innerHTML = this.emptyText;
|
||||
|
||||
this.afterRender = new EventEmitter<void>();
|
||||
|
||||
this.element.addEventListener('click', (event) => this.elementClicked(event));
|
||||
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
this.element.classList.remove('core-loading');
|
||||
|
||||
|
@ -349,6 +350,9 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
|
|||
|
||||
// Emit the afterRender output.
|
||||
this.afterRender.emit();
|
||||
if (triggerFilterRender) {
|
||||
this.filterContentRenderingComplete.emit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -402,11 +406,13 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
|
|||
this.component,
|
||||
this.componentId,
|
||||
result.siteId,
|
||||
);
|
||||
).finally(() => {
|
||||
this.filterContentRenderingComplete.emit();
|
||||
});
|
||||
}
|
||||
|
||||
this.element.classList.remove('core-disable-media-adapt');
|
||||
await this.finishRender();
|
||||
await this.finishRender(!result.options.filter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue