From aa524a41b68ef51a28c1c7bba2be4f6cab2c6637 Mon Sep 17 00:00:00 2001 From: Hieu Nguyen Dang Date: Fri, 10 May 2024 11:50:04 +0700 Subject: [PATCH] marker --- .../services/handlers/mathjaxloader.ts | 7 ++ src/addons/qtype/ddmarker/classes/ddmarker.ts | 101 +++++++++++++++++- .../component/addon-qtype-ddmarker.html | 3 +- .../qtype/ddmarker/component/ddmarker.scss | 3 + src/core/singletons/events.ts | 1 + 5 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts b/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts index 7ca996ae74c..7a0b2c76267 100644 --- a/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts +++ b/src/addons/filter/mathjaxloader/services/handlers/mathjaxloader.ts @@ -246,6 +246,13 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan 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]); + that.window.MathJax.Hub.Queue([(node: Element) => { + // The notifyFilterContentRenderingComplete event takes an Array of NodeElements/NodeList. + // We cannot create a NodeList, so we use an HTMLElement[]. + CoreEvents.trigger(CoreEvents.FILTER_CONTENT_RENDERING_COMPLETE, { + node, + }, CoreSites.getCurrentSiteId()); + }, node]); }); // Set the delay back to normal after processing. diff --git a/src/addons/qtype/ddmarker/classes/ddmarker.ts b/src/addons/qtype/ddmarker/classes/ddmarker.ts index e29bd2042ff..683bc69dbe2 100644 --- a/src/addons/qtype/ddmarker/classes/ddmarker.ts +++ b/src/addons/qtype/ddmarker/classes/ddmarker.ts @@ -15,7 +15,7 @@ import { CoreDomUtils } from '@services/utils/dom'; import { CoreTextUtils } from '@services/utils/text'; import { CoreCoordinates, CoreDom } from '@singletons/dom'; -import { CoreEventObserver } from '@singletons/events'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreLogger } from '@singletons/logger'; import { AddonQtypeDdMarkerQuestionData } from '../component/ddmarker'; import { AddonQtypeDdMarkerGraphicsApi } from './graphics_api'; @@ -700,7 +700,9 @@ export class AddonQtypeDdMarkerQuestion { setTimeout(() => { this.redrawDragsAndDrops(); }); - + CoreEvents.on(CoreEvents.FILTER_CONTENT_RENDERING_COMPLETE, (node: { node: HTMLElement }) => { + this.changeAllDragsAndDropsToFilteredContent(node.node); + }); this.afterImageLoadDone = true; this.question.loaded = true; }; @@ -719,6 +721,101 @@ export class AddonQtypeDdMarkerQuestion { }, 500); } + /** + * Modify the content drag/drop element in the same group and position them. + * + * @param {Object} filteredElement the drag/drop element that has been modified by filter. + */ + changeAllDragsAndDropsToFilteredContent(filteredElement: HTMLElement): void { + const parentIsMarker: boolean = (filteredElement + ?.parentNode as HTMLElement) + ?.closest('span.marker') instanceof HTMLElement; + const isMarker: boolean = filteredElement.classList.contains('marker'); + const root = this.container; + + // The filtered element or parent element should a drag or drop item. + if (!parentIsMarker && !isMarker) { + return; + } + if (parentIsMarker) { + filteredElement = (filteredElement?.parentNode as HTMLElement).closest('span.marker') as HTMLElement; + } + if (!root.contains(filteredElement)) { + // If the maker doesn't belong to this question + // In case we have multiple questions in the same page. + return; + } + const dragNo = this.getDragNo(filteredElement); + const choiceNo = this.getChoiceNo(filteredElement); + const listOfContainerToBeModifed: string[] = [ + 'div.draghomes .marker:not(.dragplaceholder).dragno' + dragNo + '.choice' + choiceNo, + 'div.droparea .marker:not(.dragplaceholder).dragno' + dragNo + '.choice' + choiceNo, + 'div.draghomes .marker:not(.dragplaceholder).infinite.choice' + choiceNo, + 'div.droparea .marker:not(.dragplaceholder).infinite.choice' + choiceNo, + ]; + const listOfModifiedDragDrop: HTMLElement[] = []; + const cloneElement = filteredElement.cloneNode(true) as HTMLElement; + listOfContainerToBeModifed.forEach((selector: string) => { + const elm = root.querySelector(selector); + if (elm === null) { + return; + } + const originalClass = elm.getAttribute('class'); + const originalStyle = elm.getAttribute('style'); + // Replace the class and style of the maker we want to replace for the clone. + if (originalClass) { + cloneElement.setAttribute('class', originalClass); + } + if (originalStyle) { + cloneElement.setAttribute('style', originalStyle); + } + if (!this.readOnly) { + this.draggable(cloneElement); + } + elm.insertAdjacentElement('beforebegin', cloneElement); + listOfModifiedDragDrop.push(elm as HTMLElement); + }); + if (listOfModifiedDragDrop.length > 0) { + listOfModifiedDragDrop.map(element => element.remove()); + } + } + + /** + * Get drag item number from a drag element. + * + * @param {Object} element The Drag element. + */ + getDragNo(element: HTMLElement): number | null { + return this.getClassnameNumericSuffix(element, 'dragno'); + } + + /** + * Get the choice number of an element. It is extracted from the classes. + * + * @param {Object} element to check. + * @returns Choice number. + */ + getChoiceNo(element: HTMLElement): number | null { + return this.getClassnameNumericSuffix(element, 'choice'); + } + + /** + * Get a number from a class suffix. + * + * @param {Object} node The element. + * @param {String} prefix The prefix. + * @returns The number in the class. + */ + getClassnameNumericSuffix(node: HTMLElement, prefix: string): number | null { + const classes = node.classList.value; + // Use regular expression to match the number after prefix + const regex = new RegExp(prefix + '(\\d+)'); + const match = classes.match(regex); + + // Extract the number from the matched substring + return match ? parseInt(match[1]) : null; + } + /** * Redraw all draggables and drop zones. */ diff --git a/src/addons/qtype/ddmarker/component/addon-qtype-ddmarker.html b/src/addons/qtype/ddmarker/component/addon-qtype-ddmarker.html index d800948a99c..0849487adbc 100644 --- a/src/addons/qtype/ddmarker/component/addon-qtype-ddmarker.html +++ b/src/addons/qtype/ddmarker/component/addon-qtype-ddmarker.html @@ -17,6 +17,7 @@
+ [text]="question.ddArea" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" + (afterRender)="ddAreaRendered()"/>
diff --git a/src/addons/qtype/ddmarker/component/ddmarker.scss b/src/addons/qtype/ddmarker/component/ddmarker.scss index 980cb3b5495..a19af7044ef 100644 --- a/src/addons/qtype/ddmarker/component/ddmarker.scss +++ b/src/addons/qtype/ddmarker/component/ddmarker.scss @@ -80,6 +80,9 @@ core-format-text ::ng-deep { div.ddarea { text-align: center; position: relative; + div.dd-original.d-none { + display: none; + } } div.ddarea .dropzones, div.ddarea .markertexts { diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts index cc8eab77913..0182772a170 100644 --- a/src/core/singletons/events.ts +++ b/src/core/singletons/events.ts @@ -116,6 +116,7 @@ export class CoreEvents { static readonly COMPLETE_REQUIRED_PROFILE_DATA_FINISHED = 'complete_required_profile_data_finished'; static readonly MAIN_HOME_LOADED = 'main_home_loaded'; static readonly FULL_SCREEN_CHANGED = 'full_screen_changed'; + static readonly FILTER_CONTENT_RENDERING_COMPLETE = 'core_filters_content_rendering_complete'; protected static logger = CoreLogger.getInstance('CoreEvents'); protected static observables: { [eventName: string]: Subject } = {};