forked from CIT/Vmeda.Online
		
	MOBILE-4587 qtype: Fix race condition with MathJax in D&D questions
This commit is contained in:
		
							parent
							
								
									90a356f852
								
							
						
					
					
						commit
						9081494e31
					
				@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user