Merge pull request #1717 from albertgasset/MOBILE-2795
MOBILE-2795 qtype: Fix drag and drop questions in 3.6 sitesmain
commit
49615641af
|
@ -197,9 +197,34 @@ export class AddonQtypeDdImageOrTextQuestion {
|
|||
*/
|
||||
docStructure(slot: number): AddonQtypeDdImageOrTextQuestionDocStructure {
|
||||
const topNode = <HTMLElement> this.container.querySelector('.addon-qtype-ddimageortext-container'),
|
||||
dragItemsArea = <HTMLElement> topNode.querySelector('div.dragitems'),
|
||||
doc: AddonQtypeDdImageOrTextQuestionDocStructure = {};
|
||||
|
||||
let dragItemsArea = <HTMLElement> topNode.querySelector('div.draghomes');
|
||||
|
||||
if (dragItemsArea) {
|
||||
// 3.6+ site, transform HTML so it has the same structure as in Moodle 3.5.
|
||||
|
||||
// Remove empty div.dragitems.
|
||||
topNode.querySelector('div.dragitems').remove();
|
||||
|
||||
const ddArea = topNode.querySelector('div.ddarea');
|
||||
|
||||
// Move div.dropzones to div.ddarea.
|
||||
ddArea.appendChild(topNode.querySelector('div.dropzones'));
|
||||
|
||||
// Move div.draghomes to div.ddarea and rename the class to .dragitems.
|
||||
ddArea.appendChild(dragItemsArea);
|
||||
dragItemsArea.classList.remove('draghomes');
|
||||
dragItemsArea.classList.add('dragitems');
|
||||
|
||||
// Add .dragitemhomesNNN class to drag items.
|
||||
Array.from(dragItemsArea.querySelectorAll('.draghome')).forEach((draghome, index) => {
|
||||
draghome.classList.add('dragitemhomes' + index);
|
||||
});
|
||||
} else {
|
||||
dragItemsArea = <HTMLElement> topNode.querySelector('div.dragitems');
|
||||
}
|
||||
|
||||
doc.topNode = (): HTMLElement => {
|
||||
return topNode;
|
||||
};
|
||||
|
|
|
@ -64,12 +64,21 @@ export class AddonQtypeDdImageOrTextComponent extends CoreQuestionBaseComponent
|
|||
this.question.readOnly = false;
|
||||
|
||||
if (this.question.initObjects) {
|
||||
// Moodle version <= 3.5.
|
||||
if (typeof this.question.initObjects.drops != 'undefined') {
|
||||
this.drops = this.question.initObjects.drops;
|
||||
}
|
||||
if (typeof this.question.initObjects.readonly != 'undefined') {
|
||||
this.question.readOnly = this.question.initObjects.readonly;
|
||||
}
|
||||
} else if (this.question.amdArgs) {
|
||||
// Moodle version >= 3.6.
|
||||
if (typeof this.question.amdArgs[1] != 'undefined') {
|
||||
this.question.readOnly = this.question.amdArgs[1];
|
||||
}
|
||||
if (typeof this.question.amdArgs[2] != 'undefined') {
|
||||
this.drops = this.question.amdArgs[2];
|
||||
}
|
||||
}
|
||||
|
||||
this.question.loaded = false;
|
||||
|
|
|
@ -65,9 +65,11 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
* @param {any} question The question instance.
|
||||
* @param {boolean} readOnly Whether it's read only.
|
||||
* @param {any[]} dropZones The drop zones received in the init object of the question.
|
||||
* @param {string} [imgSrc] Background image source (3.6+ sites).
|
||||
*/
|
||||
constructor(logger: CoreLoggerProvider, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider,
|
||||
protected container: HTMLElement, protected question: any, protected readOnly: boolean, protected dropZones: any[]) {
|
||||
protected container: HTMLElement, protected question: any, protected readOnly: boolean, protected dropZones: any[],
|
||||
protected imgSrc?: string) {
|
||||
this.logger = logger.getInstance('AddonQtypeDdMarkerQuestion');
|
||||
|
||||
this.graphics = new AddonQtypeDdMarkerGraphicsApi(this, this.domUtils);
|
||||
|
@ -645,7 +647,7 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
|
||||
// Wait the DOM to be rendered.
|
||||
setTimeout(() => {
|
||||
this.pollForImageLoad(question.scriptsCode, question.slot);
|
||||
this.pollForImageLoad();
|
||||
});
|
||||
|
||||
this.resizeFunction = this.redrawDragsAndDrops.bind(this);
|
||||
|
@ -706,24 +708,16 @@ export class AddonQtypeDdMarkerQuestion {
|
|||
|
||||
/**
|
||||
* Wait for the background image to be loaded.
|
||||
*
|
||||
* @param {string} scriptsCode Scripts code to fetch background image.
|
||||
* @param {number} slot Question number also named slot.
|
||||
*/
|
||||
pollForImageLoad(scriptsCode?: string, slot?: number): void {
|
||||
pollForImageLoad(): void {
|
||||
if (this.afterImageLoadDone) {
|
||||
// Already treated.
|
||||
return;
|
||||
}
|
||||
|
||||
const bgImg = this.doc.bgImg();
|
||||
|
||||
if (scriptsCode && !bgImg.src) {
|
||||
const regExp = RegExp('amd\\.init\\("q' + slot + '",[ ]*"([^"]*)"', 'g'),
|
||||
match = regExp.exec(scriptsCode);
|
||||
if (match && match.length > 1) {
|
||||
bgImg.src = match[1];
|
||||
}
|
||||
if (!bgImg.src && this.imgSrc) {
|
||||
bgImg.src = this.imgSrc;
|
||||
}
|
||||
|
||||
const imgLoaded = (): void => {
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
import { Component, OnInit, OnDestroy, Injector, ElementRef, ViewChild } from '@angular/core';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreQuestionBaseComponent } from '@core/question/classes/base-question-component';
|
||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||
import { AddonQtypeDdMarkerQuestion } from '../classes/ddmarker';
|
||||
|
||||
/**
|
||||
|
@ -30,9 +33,12 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
|
|||
protected element: HTMLElement;
|
||||
protected questionInstance: AddonQtypeDdMarkerQuestion;
|
||||
protected dropZones: any[]; // The drop zones received in the init object of the question.
|
||||
protected imgSrc: string; // Background image URL.
|
||||
protected destroyed = false;
|
||||
|
||||
constructor(protected loggerProvider: CoreLoggerProvider, injector: Injector, element: ElementRef) {
|
||||
constructor(protected loggerProvider: CoreLoggerProvider, injector: Injector, element: ElementRef,
|
||||
protected sitesProvider: CoreSitesProvider, protected urlUtils: CoreUrlUtilsProvider,
|
||||
protected filepoolProvider: CoreFilepoolProvider) {
|
||||
super(loggerProvider, 'AddonQtypeDdMarkerComponent', injector);
|
||||
|
||||
this.element = element.nativeElement;
|
||||
|
@ -72,12 +78,24 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
|
|||
this.question.readOnly = false;
|
||||
|
||||
if (this.question.initObjects) {
|
||||
// Moodle version <= 3.5.
|
||||
if (typeof this.question.initObjects.dropzones != 'undefined') {
|
||||
this.dropZones = this.question.initObjects.dropzones;
|
||||
}
|
||||
if (typeof this.question.initObjects.readonly != 'undefined') {
|
||||
this.question.readOnly = this.question.initObjects.readonly;
|
||||
}
|
||||
} else if (this.question.amdArgs) {
|
||||
// Moodle version >= 3.6.
|
||||
if (typeof this.question.amdArgs[1] != 'undefined') {
|
||||
this.imgSrc = this.question.amdArgs[1];
|
||||
}
|
||||
if (typeof this.question.amdArgs[2] != 'undefined') {
|
||||
this.question.readOnly = this.question.amdArgs[2];
|
||||
}
|
||||
if (typeof this.question.amdArgs[3] != 'undefined') {
|
||||
this.dropZones = this.question.amdArgs[3];
|
||||
}
|
||||
}
|
||||
|
||||
this.question.loaded = false;
|
||||
|
@ -88,10 +106,21 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
|
|||
*/
|
||||
questionRendered(): void {
|
||||
if (!this.destroyed) {
|
||||
this.domUtils.waitForImages(this.questionTextEl.nativeElement).then(() => {
|
||||
// Create the instance.
|
||||
this.questionInstance = new AddonQtypeDdMarkerQuestion(this.loggerProvider, this.domUtils, this.textUtils,
|
||||
this.element, this.question, this.question.readOnly, this.dropZones);
|
||||
// Download background image (3.6+ sites).
|
||||
let promise = null;
|
||||
const site = this.sitesProvider.getCurrentSite();
|
||||
if (this.imgSrc && site.canDownloadFiles() && this.urlUtils.isPluginFileUrl(this.imgSrc)) {
|
||||
promise = this.filepoolProvider.getSrcByUrl(site.id, this.imgSrc, this.component, this.componentId, 0, true, true);
|
||||
} else {
|
||||
promise = Promise.resolve(this.imgSrc);
|
||||
}
|
||||
|
||||
promise.then((imgSrc) => {
|
||||
this.domUtils.waitForImages(this.questionTextEl.nativeElement).then(() => {
|
||||
// Create the instance.
|
||||
this.questionInstance = new AddonQtypeDdMarkerQuestion(this.loggerProvider, this.domUtils, this.textUtils,
|
||||
this.element, this.question, this.question.readOnly, this.dropZones, imgSrc);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import { Injectable, Injector } from '@angular/core';
|
||||
import { CoreQuestionProvider } from '@core/question/providers/question';
|
||||
import { CoreQuestionHandler } from '@core/question/providers/delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
import { AddonQtypeDdMarkerComponent } from '../component/ddmarker';
|
||||
|
||||
/**
|
||||
|
@ -26,7 +27,7 @@ export class AddonQtypeDdMarkerHandler implements CoreQuestionHandler {
|
|||
name = 'AddonQtypeDdMarker';
|
||||
type = 'qtype_ddmarker';
|
||||
|
||||
constructor(private questionProvider: CoreQuestionProvider) { }
|
||||
constructor(private questionProvider: CoreQuestionProvider, private questionHelper: CoreQuestionHelperProvider) { }
|
||||
|
||||
/**
|
||||
* Return the name of the behaviour to use for the question.
|
||||
|
@ -106,4 +107,21 @@ export class AddonQtypeDdMarkerHandler implements CoreQuestionHandler {
|
|||
isSameResponse(question: any, prevAnswers: any, newAnswers: any): boolean {
|
||||
return this.questionProvider.compareAllAnswers(prevAnswers, newAnswers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of files that needs to be downloaded in addition to the files embedded in the HTML.
|
||||
*
|
||||
* @param {any} question Question.
|
||||
* @return {string[]} List of URLs.
|
||||
*/
|
||||
getAdditionalDownloadableFiles(question: any): string[] {
|
||||
this.questionHelper.extractQuestionScripts(question);
|
||||
|
||||
if (question.amdArgs && typeof question.amdArgs[1] !== 'undefined') {
|
||||
// Moodle 3.6+.
|
||||
return [question.amdArgs[1]];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,14 @@ export interface CoreQuestionHandler extends CoreDelegateHandler {
|
|||
* @return {boolean} Whether sequencecheck is valid.
|
||||
*/
|
||||
validateSequenceCheck?(question: any, offlineSequenceCheck: string): boolean;
|
||||
|
||||
/**
|
||||
* Get the list of files that needs to be downloaded in addition to the files embedded in the HTML.
|
||||
*
|
||||
* @param {any} question Question.
|
||||
* @return {string[]} List of URLs.
|
||||
*/
|
||||
getAdditionalDownloadableFiles?(question: any): string[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -261,4 +269,16 @@ export class CoreQuestionDelegate extends CoreDelegate {
|
|||
|
||||
return this.executeFunctionOnEnabled(type, 'validateSequenceCheck', [question, offlineSequenceCheck]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of files that needs to be downloaded in addition to the files embedded in the HTML.
|
||||
*
|
||||
* @param {any} question Question.
|
||||
* @return {string[]} List of URLs.
|
||||
*/
|
||||
getAdditionalDownloadableFiles(question: any): string[] {
|
||||
const type = this.getTypeName(question);
|
||||
|
||||
return this.executeFunctionOnEnabled(type, 'getAdditionalDownloadableFiles', [question]) || [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,7 +242,8 @@ export class CoreQuestionHelperProvider {
|
|||
*/
|
||||
extractQuestionScripts(question: any): void {
|
||||
question.scriptsCode = '';
|
||||
question.initObjects = [];
|
||||
question.initObjects = null;
|
||||
question.amdArgs = null;
|
||||
|
||||
if (question.html) {
|
||||
// Search the scripts.
|
||||
|
@ -267,7 +268,15 @@ export class CoreQuestionHelperProvider {
|
|||
initMatch = initMatch.substr(0, initMatch.length - 2);
|
||||
|
||||
// Try to convert it to an object and add it to the question.
|
||||
question.initObjects = this.textUtils.parseJSON(initMatch);
|
||||
question.initObjects = this.textUtils.parseJSON(initMatch, null);
|
||||
}
|
||||
|
||||
const amdRegExp = new RegExp('require\\(\\["qtype_' + question.type + '/question"\\], ' +
|
||||
'function\\(amd\\) \\{ amd\.init\\(("q' + question.slot + '".*?)\\); \\}\\);;', 'm');
|
||||
const amdMatch = match.match(amdRegExp);
|
||||
if (amdMatch) {
|
||||
// Try to convert the arguments to an array and add them to the question.
|
||||
question.amdArgs = this.textUtils.parseJSON('[' + amdMatch[1] + ']', null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -507,6 +516,8 @@ export class CoreQuestionHelperProvider {
|
|||
componentId = question.id;
|
||||
}
|
||||
|
||||
urls.push(...this.questionDelegate.getAdditionalDownloadableFiles(question));
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const promises = [];
|
||||
|
||||
|
|
Loading…
Reference in New Issue