MOBILE-2444 media: Prevent multiple requests and EXC_BAD_ACCESS
parent
d2b175ef0a
commit
cbf7214cc3
|
@ -27,8 +27,6 @@ import * as moment from 'moment';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AddonModLessonHelperProvider {
|
export class AddonModLessonHelperProvider {
|
||||||
|
|
||||||
protected div = document.createElement('div'); // A div element to search in HTML code.
|
|
||||||
|
|
||||||
constructor(private domUtils: CoreDomUtilsProvider, private fb: FormBuilder, private translate: TranslateService,
|
constructor(private domUtils: CoreDomUtilsProvider, private fb: FormBuilder, private translate: TranslateService,
|
||||||
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider) { }
|
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider) { }
|
||||||
|
|
||||||
|
@ -39,8 +37,9 @@ export class AddonModLessonHelperProvider {
|
||||||
* @return {{formatted: boolean, label: string, href: string}} Formatted data.
|
* @return {{formatted: boolean, label: string, href: string}} Formatted data.
|
||||||
*/
|
*/
|
||||||
formatActivityLink(activityLink: string): {formatted: boolean, label: string, href: string} {
|
formatActivityLink(activityLink: string): {formatted: boolean, label: string, href: string} {
|
||||||
this.div.innerHTML = activityLink;
|
const element = this.domUtils.convertToElement(activityLink),
|
||||||
const anchor = this.div.querySelector('a');
|
anchor = element.querySelector('a');
|
||||||
|
|
||||||
if (!anchor) {
|
if (!anchor) {
|
||||||
// Anchor not found, return the original HTML.
|
// Anchor not found, return the original HTML.
|
||||||
return {
|
return {
|
||||||
|
@ -67,11 +66,11 @@ export class AddonModLessonHelperProvider {
|
||||||
const data = {
|
const data = {
|
||||||
buttonText: '',
|
buttonText: '',
|
||||||
content: ''
|
content: ''
|
||||||
};
|
},
|
||||||
|
element = this.domUtils.convertToElement(html);
|
||||||
|
|
||||||
// Search the input button.
|
// Search the input button.
|
||||||
this.div.innerHTML = html;
|
const button = <HTMLInputElement> element.querySelector('input[type="button"]');
|
||||||
const button = <HTMLInputElement> this.div.querySelector('input[type="button"]');
|
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
// Extract the button content and remove it from the HTML.
|
// Extract the button content and remove it from the HTML.
|
||||||
|
@ -79,7 +78,7 @@ export class AddonModLessonHelperProvider {
|
||||||
button.remove();
|
button.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
data.content = this.div.innerHTML.trim();
|
data.content = element.innerHTML.trim();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -91,19 +90,19 @@ export class AddonModLessonHelperProvider {
|
||||||
* @return {any[]} List of buttons.
|
* @return {any[]} List of buttons.
|
||||||
*/
|
*/
|
||||||
getPageButtonsFromHtml(html: string): any[] {
|
getPageButtonsFromHtml(html: string): any[] {
|
||||||
const buttons = [];
|
const buttons = [],
|
||||||
|
element = this.domUtils.convertToElement(html);
|
||||||
|
|
||||||
// Get the container of the buttons if it exists.
|
// Get the container of the buttons if it exists.
|
||||||
this.div.innerHTML = html;
|
let buttonsContainer = element.querySelector('.branchbuttoncontainer');
|
||||||
let buttonsContainer = this.div.querySelector('.branchbuttoncontainer');
|
|
||||||
|
|
||||||
if (!buttonsContainer) {
|
if (!buttonsContainer) {
|
||||||
// Button container not found, might be a legacy lesson (from 1.9).
|
// Button container not found, might be a legacy lesson (from 1.9).
|
||||||
if (!this.div.querySelector('form input[type="submit"]')) {
|
if (!element.querySelector('form input[type="submit"]')) {
|
||||||
// No buttons found.
|
// No buttons found.
|
||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
buttonsContainer = this.div;
|
buttonsContainer = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
const forms = Array.from(buttonsContainer.querySelectorAll('form'));
|
const forms = Array.from(buttonsContainer.querySelectorAll('form'));
|
||||||
|
@ -144,8 +143,8 @@ export class AddonModLessonHelperProvider {
|
||||||
*/
|
*/
|
||||||
getPageContentsFromPageData(data: any): string {
|
getPageContentsFromPageData(data: any): string {
|
||||||
// Search the page contents inside the whole page HTML. Use data.pagecontent because it's filtered.
|
// Search the page contents inside the whole page HTML. Use data.pagecontent because it's filtered.
|
||||||
this.div.innerHTML = data.pagecontent;
|
const element = this.domUtils.convertToElement(data.pagecontent),
|
||||||
const contents = this.div.querySelector('.contents');
|
contents = element.querySelector('.contents');
|
||||||
|
|
||||||
if (contents) {
|
if (contents) {
|
||||||
return contents.innerHTML.trim();
|
return contents.innerHTML.trim();
|
||||||
|
@ -163,20 +162,20 @@ export class AddonModLessonHelperProvider {
|
||||||
* @return {any} Question data.
|
* @return {any} Question data.
|
||||||
*/
|
*/
|
||||||
getQuestionFromPageData(questionForm: FormGroup, pageData: any): any {
|
getQuestionFromPageData(questionForm: FormGroup, pageData: any): any {
|
||||||
const question: any = {};
|
const question: any = {},
|
||||||
|
element = this.domUtils.convertToElement(pageData.pagecontent);
|
||||||
|
|
||||||
// Get the container of the question answers if it exists.
|
// Get the container of the question answers if it exists.
|
||||||
this.div.innerHTML = pageData.pagecontent;
|
const fieldContainer = element.querySelector('.fcontainer');
|
||||||
const fieldContainer = this.div.querySelector('.fcontainer');
|
|
||||||
|
|
||||||
// Get hidden inputs and add their data to the form group.
|
// Get hidden inputs and add their data to the form group.
|
||||||
const hiddenInputs = <HTMLInputElement[]> Array.from(this.div.querySelectorAll('input[type="hidden"]'));
|
const hiddenInputs = <HTMLInputElement[]> Array.from(element.querySelectorAll('input[type="hidden"]'));
|
||||||
hiddenInputs.forEach((input) => {
|
hiddenInputs.forEach((input) => {
|
||||||
questionForm.addControl(input.name, this.fb.control(input.value));
|
questionForm.addControl(input.name, this.fb.control(input.value));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get the submit button and extract its value.
|
// Get the submit button and extract its value.
|
||||||
const submitButton = <HTMLInputElement> this.div.querySelector('input[type="submit"]');
|
const submitButton = <HTMLInputElement> element.querySelector('input[type="submit"]');
|
||||||
question.submitLabel = submitButton ? submitButton.value : this.translate.instant('addon.mod_lesson.submit');
|
question.submitLabel = submitButton ? submitButton.value : this.translate.instant('addon.mod_lesson.submit');
|
||||||
|
|
||||||
if (!fieldContainer) {
|
if (!fieldContainer) {
|
||||||
|
@ -358,28 +357,27 @@ export class AddonModLessonHelperProvider {
|
||||||
* the HTML.
|
* the HTML.
|
||||||
*/
|
*/
|
||||||
getQuestionPageAnswerDataFromHtml(html: string): any {
|
getQuestionPageAnswerDataFromHtml(html: string): any {
|
||||||
const data: any = {};
|
const data: any = {},
|
||||||
|
element = this.domUtils.convertToElement(html);
|
||||||
this.div.innerHTML = html;
|
|
||||||
|
|
||||||
// Check if it has a checkbox.
|
// Check if it has a checkbox.
|
||||||
let input = <HTMLInputElement> this.div.querySelector('input[type="checkbox"][name*="answer"]');
|
let input = <HTMLInputElement> element.querySelector('input[type="checkbox"][name*="answer"]');
|
||||||
|
|
||||||
if (input) {
|
if (input) {
|
||||||
// Truefalse or multichoice.
|
// Truefalse or multichoice.
|
||||||
data.isCheckbox = true;
|
data.isCheckbox = true;
|
||||||
data.checked = !!input.checked;
|
data.checked = !!input.checked;
|
||||||
data.name = input.name;
|
data.name = input.name;
|
||||||
data.highlight = !!this.div.querySelector('.highlight');
|
data.highlight = !!element.querySelector('.highlight');
|
||||||
|
|
||||||
input.remove();
|
input.remove();
|
||||||
data.content = this.div.innerHTML.trim();
|
data.content = element.innerHTML.trim();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it has an input text or number.
|
// Check if it has an input text or number.
|
||||||
input = <HTMLInputElement> this.div.querySelector('input[type="number"],input[type="text"]');
|
input = <HTMLInputElement> element.querySelector('input[type="number"],input[type="text"]');
|
||||||
if (input) {
|
if (input) {
|
||||||
// Short answer or numeric.
|
// Short answer or numeric.
|
||||||
data.isText = true;
|
data.isText = true;
|
||||||
|
@ -389,7 +387,7 @@ export class AddonModLessonHelperProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it has a select.
|
// Check if it has a select.
|
||||||
const select = <HTMLSelectElement> this.div.querySelector('select');
|
const select = <HTMLSelectElement> element.querySelector('select');
|
||||||
if (select && select.options) {
|
if (select && select.options) {
|
||||||
// Matching.
|
// Matching.
|
||||||
const selectedOption = select.options[select.selectedIndex];
|
const selectedOption = select.options[select.selectedIndex];
|
||||||
|
@ -402,7 +400,7 @@ export class AddonModLessonHelperProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
select.remove();
|
select.remove();
|
||||||
data.content = this.div.innerHTML.trim();
|
data.content = element.innerHTML.trim();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -477,11 +475,11 @@ export class AddonModLessonHelperProvider {
|
||||||
* @return {string} Feedback without the question text.
|
* @return {string} Feedback without the question text.
|
||||||
*/
|
*/
|
||||||
removeQuestionFromFeedback(html: string): string {
|
removeQuestionFromFeedback(html: string): string {
|
||||||
this.div.innerHTML = html;
|
const element = this.domUtils.convertToElement(html);
|
||||||
|
|
||||||
// Remove the question text.
|
// Remove the question text.
|
||||||
this.domUtils.removeElement(this.div, '.generalbox:not(.feedback):not(.correctanswer)');
|
this.domUtils.removeElement(element, '.generalbox:not(.feedback):not(.correctanswer)');
|
||||||
|
|
||||||
return this.div.innerHTML.trim();
|
return element.innerHTML.trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreGradesProvider } from '@core/grades/providers/grades';
|
import { CoreGradesProvider } from '@core/grades/providers/grades';
|
||||||
import { CoreSiteWSPreSets } from '@classes/site';
|
import { CoreSiteWSPreSets } from '@classes/site';
|
||||||
|
@ -170,10 +171,9 @@ export class AddonModLessonProvider {
|
||||||
|
|
||||||
protected ROOT_CACHE_KEY = 'mmaModLesson:';
|
protected ROOT_CACHE_KEY = 'mmaModLesson:';
|
||||||
protected logger;
|
protected logger;
|
||||||
protected div = document.createElement('div'); // A div element to search in HTML code.
|
|
||||||
|
|
||||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
||||||
private translate: TranslateService, private textUtils: CoreTextUtilsProvider,
|
private translate: TranslateService, private textUtils: CoreTextUtilsProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
private lessonOfflineProvider: AddonModLessonOfflineProvider) {
|
private lessonOfflineProvider: AddonModLessonOfflineProvider) {
|
||||||
this.logger = logger.getInstance('AddonModLessonProvider');
|
this.logger = logger.getInstance('AddonModLessonProvider');
|
||||||
|
|
||||||
|
@ -233,9 +233,9 @@ export class AddonModLessonProvider {
|
||||||
if (page.answerdata && !this.answerPageIsQuestion(page)) {
|
if (page.answerdata && !this.answerPageIsQuestion(page)) {
|
||||||
// It isn't a question page, but it can be an end of branch, etc. Check if the first answer has a button.
|
// It isn't a question page, but it can be an end of branch, etc. Check if the first answer has a button.
|
||||||
if (page.answerdata.answers && page.answerdata.answers[0]) {
|
if (page.answerdata.answers && page.answerdata.answers[0]) {
|
||||||
this.div.innerHTML = page.answerdata.answers[0][0];
|
const element = this.domUtils.convertToElement(page.answerdata.answers[0][0]);
|
||||||
|
|
||||||
return !!this.div.querySelector('input[type="button"]');
|
return !!element.querySelector('input[type="button"]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,6 @@ import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AddonModQuizHelperProvider {
|
export class AddonModQuizHelperProvider {
|
||||||
|
|
||||||
protected div = document.createElement('div'); // A div element to search in HTML code.
|
|
||||||
|
|
||||||
constructor(private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
|
constructor(private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
|
||||||
private accessRuleDelegate: AddonModQuizAccessRuleDelegate, private quizProvider: AddonModQuizProvider,
|
private accessRuleDelegate: AddonModQuizAccessRuleDelegate, private quizProvider: AddonModQuizProvider,
|
||||||
private modalCtrl: ModalController, private quizOfflineProvider: AddonModQuizOfflineProvider) { }
|
private modalCtrl: ModalController, private quizOfflineProvider: AddonModQuizOfflineProvider) { }
|
||||||
|
@ -153,9 +151,9 @@ export class AddonModQuizHelperProvider {
|
||||||
* @return {string} Question's mark.
|
* @return {string} Question's mark.
|
||||||
*/
|
*/
|
||||||
getQuestionMarkFromHtml(html: string): string {
|
getQuestionMarkFromHtml(html: string): string {
|
||||||
this.div.innerHTML = html;
|
const element = this.domUtils.convertToElement(html);
|
||||||
|
|
||||||
return this.domUtils.getContentsOfElement(this.div, '.grade');
|
return this.domUtils.getContentsOfElement(element, '.grade');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
|
@ -56,13 +57,13 @@ export class AddonModQuizProvider {
|
||||||
|
|
||||||
protected ROOT_CACHE_KEY = 'mmaModQuiz:';
|
protected ROOT_CACHE_KEY = 'mmaModQuiz:';
|
||||||
protected logger;
|
protected logger;
|
||||||
protected div = document.createElement('div'); // A div element to search in HTML code.
|
|
||||||
|
|
||||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
||||||
private translate: TranslateService, private textUtils: CoreTextUtilsProvider,
|
private translate: TranslateService, private textUtils: CoreTextUtilsProvider,
|
||||||
private gradesHelper: CoreGradesHelperProvider, private questionDelegate: CoreQuestionDelegate,
|
private gradesHelper: CoreGradesHelperProvider, private questionDelegate: CoreQuestionDelegate,
|
||||||
private filepoolProvider: CoreFilepoolProvider, private timeUtils: CoreTimeUtilsProvider,
|
private filepoolProvider: CoreFilepoolProvider, private timeUtils: CoreTimeUtilsProvider,
|
||||||
private accessRulesDelegate: AddonModQuizAccessRuleDelegate, private quizOfflineProvider: AddonModQuizOfflineProvider) {
|
private accessRulesDelegate: AddonModQuizAccessRuleDelegate, private quizOfflineProvider: AddonModQuizOfflineProvider,
|
||||||
|
private domUtils: CoreDomUtilsProvider) {
|
||||||
this.logger = logger.getInstance('AddonModQuizProvider');
|
this.logger = logger.getInstance('AddonModQuizProvider');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1480,9 +1481,9 @@ export class AddonModQuizProvider {
|
||||||
* @return {boolean} Whether it's blocked.
|
* @return {boolean} Whether it's blocked.
|
||||||
*/
|
*/
|
||||||
isQuestionBlocked(question: any): boolean {
|
isQuestionBlocked(question: any): boolean {
|
||||||
this.div.innerHTML = question.html;
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
|
|
||||||
return !!this.div.querySelector('.mod_quiz-blocked_question_warning');
|
return !!element.querySelector('.mod_quiz-blocked_question_warning');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable, Injector } from '@angular/core';
|
import { Injectable, Injector } from '@angular/core';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreQuestionHandler } from '@core/question/providers/delegate';
|
import { CoreQuestionHandler } from '@core/question/providers/delegate';
|
||||||
import { AddonQtypeNumericalHandler } from '@addon/qtype/numerical/providers/handler';
|
import { AddonQtypeNumericalHandler } from '@addon/qtype/numerical/providers/handler';
|
||||||
|
@ -27,7 +28,8 @@ export class AddonQtypeCalculatedHandler implements CoreQuestionHandler {
|
||||||
name = 'AddonQtypeCalculated';
|
name = 'AddonQtypeCalculated';
|
||||||
type = 'qtype_calculated';
|
type = 'qtype_calculated';
|
||||||
|
|
||||||
constructor(private utils: CoreUtilsProvider, private numericalHandler: AddonQtypeNumericalHandler) { }
|
constructor(private utils: CoreUtilsProvider, private numericalHandler: AddonQtypeNumericalHandler,
|
||||||
|
private domUtils: CoreDomUtilsProvider) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Component to use to display the question.
|
* Return the Component to use to display the question.
|
||||||
|
@ -120,9 +122,8 @@ export class AddonQtypeCalculatedHandler implements CoreQuestionHandler {
|
||||||
* @return {boolean} Whether the question requires units.
|
* @return {boolean} Whether the question requires units.
|
||||||
*/
|
*/
|
||||||
requiresUnits(question: any): boolean {
|
requiresUnits(question: any): boolean {
|
||||||
const div = document.createElement('div');
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
div.innerHTML = question.html;
|
|
||||||
|
|
||||||
return !!(div.querySelector('select[name*=unit]') || div.querySelector('input[type="radio"]'));
|
return !!(element.querySelector('select[name*=unit]') || element.querySelector('input[type="radio"]'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,13 +47,12 @@ export class AddonQtypeDdImageOrTextComponent extends CoreQuestionBaseComponent
|
||||||
return this.questionHelper.showComponentError(this.onAbort);
|
return this.questionHelper.showComponentError(this.onAbort);
|
||||||
}
|
}
|
||||||
|
|
||||||
const div = document.createElement('div');
|
const element = this.domUtils.convertToElement(this.question.html);
|
||||||
div.innerHTML = this.question.html;
|
|
||||||
|
|
||||||
// Get D&D area and question text.
|
// Get D&D area and question text.
|
||||||
const ddArea = div.querySelector('.ddarea');
|
const ddArea = element.querySelector('.ddarea');
|
||||||
|
|
||||||
this.question.text = this.domUtils.getContentsOfElement(div, '.qtext');
|
this.question.text = this.domUtils.getContentsOfElement(element, '.qtext');
|
||||||
if (!ddArea || typeof this.question.text == 'undefined') {
|
if (!ddArea || typeof this.question.text == 'undefined') {
|
||||||
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
||||||
|
|
||||||
|
|
|
@ -47,14 +47,13 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
|
||||||
return this.questionHelper.showComponentError(this.onAbort);
|
return this.questionHelper.showComponentError(this.onAbort);
|
||||||
}
|
}
|
||||||
|
|
||||||
const div = document.createElement('div');
|
const element = this.domUtils.convertToElement(this.question.html);
|
||||||
div.innerHTML = this.question.html;
|
|
||||||
|
|
||||||
// Get D&D area, form and question text.
|
// Get D&D area, form and question text.
|
||||||
const ddArea = div.querySelector('.ddarea'),
|
const ddArea = element.querySelector('.ddarea'),
|
||||||
ddForm = div.querySelector('.ddform');
|
ddForm = element.querySelector('.ddform');
|
||||||
|
|
||||||
this.question.text = this.domUtils.getContentsOfElement(div, '.qtext');
|
this.question.text = this.domUtils.getContentsOfElement(element, '.qtext');
|
||||||
if (!ddArea || !ddForm || typeof this.question.text == 'undefined') {
|
if (!ddArea || !ddForm || typeof this.question.text == 'undefined') {
|
||||||
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
||||||
|
|
||||||
|
@ -64,7 +63,7 @@ export class AddonQtypeDdMarkerComponent extends CoreQuestionBaseComponent imple
|
||||||
// Build the D&D area HTML.
|
// Build the D&D area HTML.
|
||||||
this.question.ddArea = ddArea.outerHTML;
|
this.question.ddArea = ddArea.outerHTML;
|
||||||
|
|
||||||
const wrongParts = div.querySelector('.wrongparts');
|
const wrongParts = element.querySelector('.wrongparts');
|
||||||
if (wrongParts) {
|
if (wrongParts) {
|
||||||
this.question.ddArea += wrongParts.outerHTML;
|
this.question.ddArea += wrongParts.outerHTML;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,17 +47,16 @@ export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent impleme
|
||||||
return this.questionHelper.showComponentError(this.onAbort);
|
return this.questionHelper.showComponentError(this.onAbort);
|
||||||
}
|
}
|
||||||
|
|
||||||
const div = document.createElement('div');
|
const element = this.domUtils.convertToElement(this.question.html);
|
||||||
div.innerHTML = this.question.html;
|
|
||||||
|
|
||||||
// Replace Moodle's correct/incorrect and feedback classes with our own.
|
// Replace Moodle's correct/incorrect and feedback classes with our own.
|
||||||
this.questionHelper.replaceCorrectnessClasses(div);
|
this.questionHelper.replaceCorrectnessClasses(element);
|
||||||
this.questionHelper.replaceFeedbackClasses(div);
|
this.questionHelper.replaceFeedbackClasses(element);
|
||||||
|
|
||||||
// Treat the correct/incorrect icons.
|
// Treat the correct/incorrect icons.
|
||||||
this.questionHelper.treatCorrectnessIcons(div);
|
this.questionHelper.treatCorrectnessIcons(element);
|
||||||
|
|
||||||
const answerContainer = div.querySelector('.answercontainer');
|
const answerContainer = element.querySelector('.answercontainer');
|
||||||
if (!answerContainer) {
|
if (!answerContainer) {
|
||||||
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
||||||
|
|
||||||
|
@ -67,7 +66,7 @@ export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent impleme
|
||||||
this.question.readOnly = answerContainer.classList.contains('readonly');
|
this.question.readOnly = answerContainer.classList.contains('readonly');
|
||||||
this.question.answers = answerContainer.outerHTML;
|
this.question.answers = answerContainer.outerHTML;
|
||||||
|
|
||||||
this.question.text = this.domUtils.getContentsOfElement(div, '.qtext');
|
this.question.text = this.domUtils.getContentsOfElement(element, '.qtext');
|
||||||
if (typeof this.question.text == 'undefined') {
|
if (typeof this.question.text == 'undefined') {
|
||||||
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
||||||
|
|
||||||
|
@ -75,7 +74,7 @@ export class AddonQtypeDdwtosComponent extends CoreQuestionBaseComponent impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the inputs where the answers will be stored and add them to the question text.
|
// Get the inputs where the answers will be stored and add them to the question text.
|
||||||
const inputEls = <HTMLElement[]> Array.from(div.querySelectorAll('input[type="hidden"]:not([name*=sequencecheck])'));
|
const inputEls = <HTMLElement[]> Array.from(element.querySelectorAll('input[type="hidden"]:not([name*=sequencecheck])'));
|
||||||
|
|
||||||
inputEls.forEach((inputEl) => {
|
inputEls.forEach((inputEl) => {
|
||||||
this.question.text += inputEl.outerHTML;
|
this.question.text += inputEl.outerHTML;
|
||||||
|
|
|
@ -33,10 +33,10 @@ export class AddonQtypeDescriptionComponent extends CoreQuestionBaseComponent im
|
||||||
* Component being initialized.
|
* Component being initialized.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
const questionDiv = this.initComponent();
|
const questionEl = this.initComponent();
|
||||||
if (questionDiv) {
|
if (questionEl) {
|
||||||
// Get the "seen" hidden input.
|
// Get the "seen" hidden input.
|
||||||
const input = <HTMLInputElement> questionDiv.querySelector('input[type="hidden"][name*=seen]');
|
const input = <HTMLInputElement> questionEl.querySelector('input[type="hidden"][name*=seen]');
|
||||||
if (input) {
|
if (input) {
|
||||||
this.question.seenInput = {
|
this.question.seenInput = {
|
||||||
name: input.name,
|
name: input.name,
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
import { Injectable, Injector } from '@angular/core';
|
import { Injectable, Injector } from '@angular/core';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreQuestionHandler } from '@core/question/providers/delegate';
|
import { CoreQuestionHandler } from '@core/question/providers/delegate';
|
||||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||||
|
@ -28,10 +29,8 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
|
||||||
name = 'AddonQtypeEssay';
|
name = 'AddonQtypeEssay';
|
||||||
type = 'qtype_essay';
|
type = 'qtype_essay';
|
||||||
|
|
||||||
protected div = document.createElement('div'); // A div element to search in HTML code.
|
|
||||||
|
|
||||||
constructor(private utils: CoreUtilsProvider, private questionHelper: CoreQuestionHelperProvider,
|
constructor(private utils: CoreUtilsProvider, private questionHelper: CoreQuestionHelperProvider,
|
||||||
private textUtils: CoreTextUtilsProvider) { }
|
private textUtils: CoreTextUtilsProvider, private domUtils: CoreDomUtilsProvider) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the name of the behaviour to use for the question.
|
* Return the name of the behaviour to use for the question.
|
||||||
|
@ -65,14 +64,14 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
|
||||||
* @return {string} Prevent submit message. Undefined or empty if can be submitted.
|
* @return {string} Prevent submit message. Undefined or empty if can be submitted.
|
||||||
*/
|
*/
|
||||||
getPreventSubmitMessage(question: any): string {
|
getPreventSubmitMessage(question: any): string {
|
||||||
this.div.innerHTML = question.html;
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
|
|
||||||
if (this.div.querySelector('div[id*=filemanager]')) {
|
if (element.querySelector('div[id*=filemanager]')) {
|
||||||
// The question allows attachments. Since the app cannot attach files yet we will prevent submitting the question.
|
// The question allows attachments. Since the app cannot attach files yet we will prevent submitting the question.
|
||||||
return 'core.question.errorattachmentsnotsupported';
|
return 'core.question.errorattachmentsnotsupported';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.questionHelper.hasDraftFileUrls(this.div.innerHTML)) {
|
if (this.questionHelper.hasDraftFileUrls(element.innerHTML)) {
|
||||||
return 'core.question.errorinlinefilesnotsupported';
|
return 'core.question.errorinlinefilesnotsupported';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,10 +84,10 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
|
||||||
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
|
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
|
||||||
*/
|
*/
|
||||||
isCompleteResponse(question: any, answers: any): number {
|
isCompleteResponse(question: any, answers: any): number {
|
||||||
this.div.innerHTML = question.html;
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
|
|
||||||
const hasInlineText = answers['answer'] && answers['answer'] !== '',
|
const hasInlineText = answers['answer'] && answers['answer'] !== '',
|
||||||
allowsAttachments = !!this.div.querySelector('div[id*=filemanager]');
|
allowsAttachments = !!element.querySelector('div[id*=filemanager]');
|
||||||
|
|
||||||
if (!allowsAttachments) {
|
if (!allowsAttachments) {
|
||||||
return hasInlineText ? 1 : 0;
|
return hasInlineText ? 1 : 0;
|
||||||
|
@ -141,10 +140,10 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
|
||||||
* @return {void|Promise<any>} Return a promise resolved when done if async, void if sync.
|
* @return {void|Promise<any>} Return a promise resolved when done if async, void if sync.
|
||||||
*/
|
*/
|
||||||
prepareAnswers(question: any, answers: any, offline: boolean, siteId?: string): void | Promise<any> {
|
prepareAnswers(question: any, answers: any, offline: boolean, siteId?: string): void | Promise<any> {
|
||||||
this.div.innerHTML = question.html;
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
|
|
||||||
// Search the textarea to get its name.
|
// Search the textarea to get its name.
|
||||||
const textarea = <HTMLTextAreaElement> this.div.querySelector('textarea[name*=_answer]');
|
const textarea = <HTMLTextAreaElement> element.querySelector('textarea[name*=_answer]');
|
||||||
|
|
||||||
if (textarea && typeof answers[textarea.name] != 'undefined') {
|
if (textarea && typeof answers[textarea.name] != 'undefined') {
|
||||||
// Add some HTML to the text if needed.
|
// Add some HTML to the text if needed.
|
||||||
|
|
|
@ -51,12 +51,12 @@ export class CoreQuestionBaseComponent {
|
||||||
*/
|
*/
|
||||||
initCalculatedComponent(): void | HTMLElement {
|
initCalculatedComponent(): void | HTMLElement {
|
||||||
// Treat the input text first.
|
// Treat the input text first.
|
||||||
const questionDiv = this.initInputTextComponent();
|
const questionEl = this.initInputTextComponent();
|
||||||
if (questionDiv) {
|
if (questionEl) {
|
||||||
|
|
||||||
// Check if the question has a select for units.
|
// Check if the question has a select for units.
|
||||||
const selectModel: any = {},
|
const selectModel: any = {},
|
||||||
select = <HTMLSelectElement> questionDiv.querySelector('select[name*=unit]'),
|
select = <HTMLSelectElement> questionEl.querySelector('select[name*=unit]'),
|
||||||
options = select && Array.from(select.querySelectorAll('option'));
|
options = select && Array.from(select.querySelectorAll('option'));
|
||||||
|
|
||||||
if (select && options && options.length) {
|
if (select && options && options.length) {
|
||||||
|
@ -94,24 +94,24 @@ export class CoreQuestionBaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the accessibility label.
|
// Get the accessibility label.
|
||||||
const accessibilityLabel = questionDiv.querySelector('label[for="' + select.id + '"]');
|
const accessibilityLabel = questionEl.querySelector('label[for="' + select.id + '"]');
|
||||||
selectModel.accessibilityLabel = accessibilityLabel && accessibilityLabel.innerHTML;
|
selectModel.accessibilityLabel = accessibilityLabel && accessibilityLabel.innerHTML;
|
||||||
|
|
||||||
this.question.select = selectModel;
|
this.question.select = selectModel;
|
||||||
|
|
||||||
// Check which one should be displayed first: the select or the input.
|
// Check which one should be displayed first: the select or the input.
|
||||||
const input = questionDiv.querySelector('input[type="text"][name*=answer]');
|
const input = questionEl.querySelector('input[type="text"][name*=answer]');
|
||||||
this.question.selectFirst =
|
this.question.selectFirst =
|
||||||
questionDiv.innerHTML.indexOf(input.outerHTML) > questionDiv.innerHTML.indexOf(select.outerHTML);
|
questionEl.innerHTML.indexOf(input.outerHTML) > questionEl.innerHTML.indexOf(select.outerHTML);
|
||||||
|
|
||||||
return questionDiv;
|
return questionEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the question has radio buttons for units.
|
// Check if the question has radio buttons for units.
|
||||||
const radios = <HTMLInputElement[]> Array.from(questionDiv.querySelectorAll('input[type="radio"]'));
|
const radios = <HTMLInputElement[]> Array.from(questionEl.querySelectorAll('input[type="radio"]'));
|
||||||
if (!radios.length) {
|
if (!radios.length) {
|
||||||
// No select and no radio buttons. The units need to be entered in the input text.
|
// No select and no radio buttons. The units need to be entered in the input text.
|
||||||
return questionDiv;
|
return questionEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.question.options = [];
|
this.question.options = [];
|
||||||
|
@ -126,7 +126,7 @@ export class CoreQuestionBaseComponent {
|
||||||
disabled: radioEl.disabled
|
disabled: radioEl.disabled
|
||||||
},
|
},
|
||||||
// Get the label with the question text.
|
// Get the label with the question text.
|
||||||
label = <HTMLElement> questionDiv.querySelector('label[for="' + option.id + '"]');
|
label = <HTMLElement> questionEl.querySelector('label[for="' + option.id + '"]');
|
||||||
|
|
||||||
this.question.optionsName = option.name;
|
this.question.optionsName = option.name;
|
||||||
|
|
||||||
|
@ -154,9 +154,9 @@ export class CoreQuestionBaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check which one should be displayed first: the options or the input.
|
// Check which one should be displayed first: the options or the input.
|
||||||
const input = questionDiv.querySelector('input[type="text"][name*=answer]');
|
const input = questionEl.querySelector('input[type="text"][name*=answer]');
|
||||||
this.question.optionsFirst =
|
this.question.optionsFirst =
|
||||||
questionDiv.innerHTML.indexOf(input.outerHTML) > questionDiv.innerHTML.indexOf(radios[0].outerHTML);
|
questionEl.innerHTML.indexOf(input.outerHTML) > questionEl.innerHTML.indexOf(radios[0].outerHTML);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,18 +172,17 @@ export class CoreQuestionBaseComponent {
|
||||||
return this.questionHelper.showComponentError(this.onAbort);
|
return this.questionHelper.showComponentError(this.onAbort);
|
||||||
}
|
}
|
||||||
|
|
||||||
const div = document.createElement('div');
|
const element = this.domUtils.convertToElement(this.question.html);
|
||||||
div.innerHTML = this.question.html;
|
|
||||||
|
|
||||||
// Extract question text.
|
// Extract question text.
|
||||||
this.question.text = this.domUtils.getContentsOfElement(div, '.qtext');
|
this.question.text = this.domUtils.getContentsOfElement(element, '.qtext');
|
||||||
if (typeof this.question.text == 'undefined') {
|
if (typeof this.question.text == 'undefined') {
|
||||||
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
||||||
|
|
||||||
return this.questionHelper.showComponentError(this.onAbort);
|
return this.questionHelper.showComponentError(this.onAbort);
|
||||||
}
|
}
|
||||||
|
|
||||||
return div;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -192,24 +191,24 @@ export class CoreQuestionBaseComponent {
|
||||||
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
||||||
*/
|
*/
|
||||||
initEssayComponent(): void | HTMLElement {
|
initEssayComponent(): void | HTMLElement {
|
||||||
const questionDiv = this.initComponent();
|
const questionEl = this.initComponent();
|
||||||
|
|
||||||
if (questionDiv) {
|
if (questionEl) {
|
||||||
// First search the textarea.
|
// First search the textarea.
|
||||||
const textarea = <HTMLTextAreaElement> questionDiv.querySelector('textarea[name*=_answer]');
|
const textarea = <HTMLTextAreaElement> questionEl.querySelector('textarea[name*=_answer]');
|
||||||
this.question.allowsAttachments = !!questionDiv.querySelector('div[id*=filemanager]');
|
this.question.allowsAttachments = !!questionEl.querySelector('div[id*=filemanager]');
|
||||||
this.question.isMonospaced = !!questionDiv.querySelector('.qtype_essay_monospaced');
|
this.question.isMonospaced = !!questionEl.querySelector('.qtype_essay_monospaced');
|
||||||
this.question.isPlainText = this.question.isMonospaced || !!questionDiv.querySelector('.qtype_essay_plain');
|
this.question.isPlainText = this.question.isMonospaced || !!questionEl.querySelector('.qtype_essay_plain');
|
||||||
this.question.hasDraftFiles = this.questionHelper.hasDraftFileUrls(questionDiv.innerHTML);
|
this.question.hasDraftFiles = this.questionHelper.hasDraftFileUrls(questionEl.innerHTML);
|
||||||
|
|
||||||
if (!textarea) {
|
if (!textarea) {
|
||||||
// Textarea not found, we might be in review. Search the answer and the attachments.
|
// Textarea not found, we might be in review. Search the answer and the attachments.
|
||||||
this.question.answer = this.domUtils.getContentsOfElement(questionDiv, '.qtype_essay_response');
|
this.question.answer = this.domUtils.getContentsOfElement(questionEl, '.qtype_essay_response');
|
||||||
this.question.attachments = this.questionHelper.getQuestionAttachmentsFromHtml(
|
this.question.attachments = this.questionHelper.getQuestionAttachmentsFromHtml(
|
||||||
this.domUtils.getContentsOfElement(questionDiv, '.attachments'));
|
this.domUtils.getContentsOfElement(questionEl, '.attachments'));
|
||||||
} else {
|
} else {
|
||||||
// Textarea found.
|
// Textarea found.
|
||||||
const input = <HTMLInputElement> questionDiv.querySelector('input[type="hidden"][name*=answerformat]'),
|
const input = <HTMLInputElement> questionEl.querySelector('input[type="hidden"][name*=answerformat]'),
|
||||||
content = textarea.innerHTML;
|
content = textarea.innerHTML;
|
||||||
|
|
||||||
this.question.textarea = {
|
this.question.textarea = {
|
||||||
|
@ -241,11 +240,10 @@ export class CoreQuestionBaseComponent {
|
||||||
return this.questionHelper.showComponentError(this.onAbort);
|
return this.questionHelper.showComponentError(this.onAbort);
|
||||||
}
|
}
|
||||||
|
|
||||||
const div = document.createElement('div');
|
const element = this.domUtils.convertToElement(this.question.html);
|
||||||
div.innerHTML = this.question.html;
|
|
||||||
|
|
||||||
// Get question content.
|
// Get question content.
|
||||||
const content = <HTMLElement> div.querySelector(contentSelector);
|
const content = <HTMLElement> element.querySelector(contentSelector);
|
||||||
if (!content) {
|
if (!content) {
|
||||||
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
this.logger.warn('Aborting because of an error parsing question.', this.question.name);
|
||||||
|
|
||||||
|
@ -257,11 +255,11 @@ export class CoreQuestionBaseComponent {
|
||||||
this.domUtils.removeElement(content, '.validationerror');
|
this.domUtils.removeElement(content, '.validationerror');
|
||||||
|
|
||||||
// Replace Moodle's correct/incorrect and feedback classes with our own.
|
// Replace Moodle's correct/incorrect and feedback classes with our own.
|
||||||
this.questionHelper.replaceCorrectnessClasses(div);
|
this.questionHelper.replaceCorrectnessClasses(element);
|
||||||
this.questionHelper.replaceFeedbackClasses(div);
|
this.questionHelper.replaceFeedbackClasses(element);
|
||||||
|
|
||||||
// Treat the correct/incorrect icons.
|
// Treat the correct/incorrect icons.
|
||||||
this.questionHelper.treatCorrectnessIcons(div);
|
this.questionHelper.treatCorrectnessIcons(element);
|
||||||
|
|
||||||
// Set the question text.
|
// Set the question text.
|
||||||
this.question.text = content.innerHTML;
|
this.question.text = content.innerHTML;
|
||||||
|
@ -273,10 +271,10 @@ export class CoreQuestionBaseComponent {
|
||||||
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
||||||
*/
|
*/
|
||||||
initInputTextComponent(): void | HTMLElement {
|
initInputTextComponent(): void | HTMLElement {
|
||||||
const questionDiv = this.initComponent();
|
const questionEl = this.initComponent();
|
||||||
if (questionDiv) {
|
if (questionEl) {
|
||||||
// Get the input element.
|
// Get the input element.
|
||||||
const input = <HTMLInputElement> questionDiv.querySelector('input[type="text"][name*=answer]');
|
const input = <HTMLInputElement> questionEl.querySelector('input[type="text"][name*=answer]');
|
||||||
if (!input) {
|
if (!input) {
|
||||||
this.logger.warn('Aborting because couldn\'t find input.', this.question.name);
|
this.logger.warn('Aborting because couldn\'t find input.', this.question.name);
|
||||||
|
|
||||||
|
@ -302,7 +300,7 @@ export class CoreQuestionBaseComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return questionDiv;
|
return questionEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -311,11 +309,11 @@ export class CoreQuestionBaseComponent {
|
||||||
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
||||||
*/
|
*/
|
||||||
initMatchComponent(): void | HTMLElement {
|
initMatchComponent(): void | HTMLElement {
|
||||||
const questionDiv = this.initComponent();
|
const questionEl = this.initComponent();
|
||||||
|
|
||||||
if (questionDiv) {
|
if (questionEl) {
|
||||||
// Find rows.
|
// Find rows.
|
||||||
const rows = Array.from(questionDiv.querySelectorAll('table.answer tr'));
|
const rows = Array.from(questionEl.querySelectorAll('table.answer tr'));
|
||||||
if (!rows || !rows.length) {
|
if (!rows || !rows.length) {
|
||||||
this.logger.warn('Aborting because couldn\'t find any row.', this.question.name);
|
this.logger.warn('Aborting because couldn\'t find any row.', this.question.name);
|
||||||
|
|
||||||
|
@ -394,7 +392,7 @@ export class CoreQuestionBaseComponent {
|
||||||
this.question.loaded = true;
|
this.question.loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return questionDiv;
|
return questionEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,19 +401,19 @@ export class CoreQuestionBaseComponent {
|
||||||
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
* @return {void|HTMLElement} Element containing the question HTML, void if the data is not valid.
|
||||||
*/
|
*/
|
||||||
initMultichoiceComponent(): void | HTMLElement {
|
initMultichoiceComponent(): void | HTMLElement {
|
||||||
const questionDiv = this.initComponent();
|
const questionEl = this.initComponent();
|
||||||
|
|
||||||
if (questionDiv) {
|
if (questionEl) {
|
||||||
|
|
||||||
// Get the prompt.
|
// Get the prompt.
|
||||||
this.question.prompt = this.domUtils.getContentsOfElement(questionDiv, '.prompt');
|
this.question.prompt = this.domUtils.getContentsOfElement(questionEl, '.prompt');
|
||||||
|
|
||||||
// Search radio buttons first (single choice).
|
// Search radio buttons first (single choice).
|
||||||
let options = <HTMLInputElement[]> Array.from(questionDiv.querySelectorAll('input[type="radio"]'));
|
let options = <HTMLInputElement[]> Array.from(questionEl.querySelectorAll('input[type="radio"]'));
|
||||||
if (!options || !options.length) {
|
if (!options || !options.length) {
|
||||||
// Radio buttons not found, it should be a multi answer. Search for checkbox.
|
// Radio buttons not found, it should be a multi answer. Search for checkbox.
|
||||||
this.question.multi = true;
|
this.question.multi = true;
|
||||||
options = <HTMLInputElement[]> Array.from(questionDiv.querySelectorAll('input[type="checkbox"]'));
|
options = <HTMLInputElement[]> Array.from(questionEl.querySelectorAll('input[type="checkbox"]'));
|
||||||
|
|
||||||
if (!options || !options.length) {
|
if (!options || !options.length) {
|
||||||
// No checkbox found either. Abort.
|
// No checkbox found either. Abort.
|
||||||
|
@ -441,7 +439,7 @@ export class CoreQuestionBaseComponent {
|
||||||
this.question.optionsName = option.name;
|
this.question.optionsName = option.name;
|
||||||
|
|
||||||
// Get the label with the question text.
|
// Get the label with the question text.
|
||||||
const label = questionDiv.querySelector('label[for="' + option.id + '"]');
|
const label = questionEl.querySelector('label[for="' + option.id + '"]');
|
||||||
if (label) {
|
if (label) {
|
||||||
option.text = label.innerHTML;
|
option.text = label.innerHTML;
|
||||||
|
|
||||||
|
@ -483,6 +481,6 @@ export class CoreQuestionBaseComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return questionDiv;
|
return questionEl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ import { CoreQuestionDelegate } from './delegate';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CoreQuestionHelperProvider {
|
export class CoreQuestionHelperProvider {
|
||||||
protected lastErrorShown = 0;
|
protected lastErrorShown = 0;
|
||||||
protected div = document.createElement('div'); // A div element to search in HTML code.
|
|
||||||
|
|
||||||
constructor(private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider,
|
constructor(private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider,
|
||||||
private questionProvider: CoreQuestionProvider, private sitesProvider: CoreSitesProvider,
|
private questionProvider: CoreQuestionProvider, private sitesProvider: CoreSitesProvider,
|
||||||
|
@ -70,15 +69,15 @@ export class CoreQuestionHelperProvider {
|
||||||
extractQbehaviourButtons(question: any, selector?: string): void {
|
extractQbehaviourButtons(question: any, selector?: string): void {
|
||||||
selector = selector || '.im-controls input[type="submit"]';
|
selector = selector || '.im-controls input[type="submit"]';
|
||||||
|
|
||||||
this.div.innerHTML = question.html;
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
|
|
||||||
// Search the buttons.
|
// Search the buttons.
|
||||||
const buttons = <HTMLInputElement[]> Array.from(this.div.querySelectorAll(selector));
|
const buttons = <HTMLInputElement[]> Array.from(element.querySelectorAll(selector));
|
||||||
buttons.forEach((button) => {
|
buttons.forEach((button) => {
|
||||||
this.addBehaviourButton(question, button);
|
this.addBehaviourButton(question, button);
|
||||||
});
|
});
|
||||||
|
|
||||||
question.html = this.div.innerHTML;
|
question.html = element.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,9 +90,9 @@ export class CoreQuestionHelperProvider {
|
||||||
* @return {boolean} Wether the certainty is found.
|
* @return {boolean} Wether the certainty is found.
|
||||||
*/
|
*/
|
||||||
extractQbehaviourCBM(question: any): boolean {
|
extractQbehaviourCBM(question: any): boolean {
|
||||||
this.div.innerHTML = question.html;
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
|
|
||||||
const labels = Array.from(this.div.querySelectorAll('.im-controls .certaintychoices label[for*="certainty"]'));
|
const labels = Array.from(element.querySelectorAll('.im-controls .certaintychoices label[for*="certainty"]'));
|
||||||
question.behaviourCertaintyOptions = [];
|
question.behaviourCertaintyOptions = [];
|
||||||
|
|
||||||
labels.forEach((label) => {
|
labels.forEach((label) => {
|
||||||
|
@ -158,10 +157,10 @@ export class CoreQuestionHelperProvider {
|
||||||
* @return {boolean} Whether the seen input is found.
|
* @return {boolean} Whether the seen input is found.
|
||||||
*/
|
*/
|
||||||
extractQbehaviourSeenInput(question: any): boolean {
|
extractQbehaviourSeenInput(question: any): boolean {
|
||||||
this.div.innerHTML = question.html;
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
|
|
||||||
// Search the "seen" input.
|
// Search the "seen" input.
|
||||||
const seenInput = <HTMLInputElement> this.div.querySelector('input[type="hidden"][name*=seen]');
|
const seenInput = <HTMLInputElement> element.querySelector('input[type="hidden"][name*=seen]');
|
||||||
if (seenInput) {
|
if (seenInput) {
|
||||||
// Get the data and remove the input.
|
// Get the data and remove the input.
|
||||||
question.behaviourSeenInput = {
|
question.behaviourSeenInput = {
|
||||||
|
@ -169,7 +168,7 @@ export class CoreQuestionHelperProvider {
|
||||||
value: seenInput.value
|
value: seenInput.value
|
||||||
};
|
};
|
||||||
seenInput.parentElement.removeChild(seenInput);
|
seenInput.parentElement.removeChild(seenInput);
|
||||||
question.html = this.div.innerHTML;
|
question.html = element.innerHTML;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -214,9 +213,9 @@ export class CoreQuestionHelperProvider {
|
||||||
* @param {string} attrName Name of the attribute to store the HTML in.
|
* @param {string} attrName Name of the attribute to store the HTML in.
|
||||||
*/
|
*/
|
||||||
protected extractQuestionLastElementNotInContent(question: any, selector: string, attrName: string): void {
|
protected extractQuestionLastElementNotInContent(question: any, selector: string, attrName: string): void {
|
||||||
this.div.innerHTML = question.html;
|
const element = this.domUtils.convertToElement(question.html);
|
||||||
|
|
||||||
const matches = <HTMLElement[]> Array.from(this.div.querySelectorAll(selector));
|
const matches = <HTMLElement[]> Array.from(element.querySelectorAll(selector));
|
||||||
|
|
||||||
// Get the last element and check it's not in the question contents.
|
// Get the last element and check it's not in the question contents.
|
||||||
let last = matches.pop();
|
let last = matches.pop();
|
||||||
|
@ -225,7 +224,7 @@ export class CoreQuestionHelperProvider {
|
||||||
// Not in question contents. Add it to a separate attribute and remove it from the HTML.
|
// Not in question contents. Add it to a separate attribute and remove it from the HTML.
|
||||||
question[attrName] = last.innerHTML;
|
question[attrName] = last.innerHTML;
|
||||||
last.parentElement.removeChild(last);
|
last.parentElement.removeChild(last);
|
||||||
question.html = this.div.innerHTML;
|
question.html = element.innerHTML;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -283,11 +282,10 @@ export class CoreQuestionHelperProvider {
|
||||||
* @return {any} Object where the keys are the names.
|
* @return {any} Object where the keys are the names.
|
||||||
*/
|
*/
|
||||||
getAllInputNamesFromHtml(html: string): any {
|
getAllInputNamesFromHtml(html: string): any {
|
||||||
const form = document.createElement('form'),
|
const element = this.domUtils.convertToElement('<form>' + html + '</form>'),
|
||||||
|
form = <HTMLFormElement> element.children[0],
|
||||||
answers = {};
|
answers = {};
|
||||||
|
|
||||||
form.innerHTML = html;
|
|
||||||
|
|
||||||
// Search all input elements.
|
// Search all input elements.
|
||||||
Array.from(form.elements).forEach((element: HTMLInputElement) => {
|
Array.from(form.elements).forEach((element: HTMLInputElement) => {
|
||||||
const name = element.name || '';
|
const name = element.name || '';
|
||||||
|
@ -350,13 +348,13 @@ export class CoreQuestionHelperProvider {
|
||||||
* @return {Object[]} Attachments.
|
* @return {Object[]} Attachments.
|
||||||
*/
|
*/
|
||||||
getQuestionAttachmentsFromHtml(html: string): any[] {
|
getQuestionAttachmentsFromHtml(html: string): any[] {
|
||||||
this.div.innerHTML = html;
|
const element = this.domUtils.convertToElement(html);
|
||||||
|
|
||||||
// Remove the filemanager (area to attach files to a question).
|
// Remove the filemanager (area to attach files to a question).
|
||||||
this.domUtils.removeElement(this.div, 'div[id*=filemanager]');
|
this.domUtils.removeElement(element, 'div[id*=filemanager]');
|
||||||
|
|
||||||
// Search the anchors.
|
// Search the anchors.
|
||||||
const anchors = Array.from(this.div.querySelectorAll('a')),
|
const anchors = Array.from(element.querySelectorAll('a')),
|
||||||
attachments = [];
|
attachments = [];
|
||||||
|
|
||||||
anchors.forEach((anchor) => {
|
anchors.forEach((anchor) => {
|
||||||
|
@ -383,10 +381,10 @@ export class CoreQuestionHelperProvider {
|
||||||
*/
|
*/
|
||||||
getQuestionSequenceCheckFromHtml(html: string): {name: string, value: string} {
|
getQuestionSequenceCheckFromHtml(html: string): {name: string, value: string} {
|
||||||
if (html) {
|
if (html) {
|
||||||
this.div.innerHTML = html;
|
const element = this.domUtils.convertToElement(html);
|
||||||
|
|
||||||
// Search the input holding the sequencecheck.
|
// Search the input holding the sequencecheck.
|
||||||
const input = <HTMLInputElement> this.div.querySelector('input[name*=sequencecheck]');
|
const input = <HTMLInputElement> element.querySelector('input[name*=sequencecheck]');
|
||||||
if (input && typeof input.name != 'undefined' && typeof input.value != 'undefined') {
|
if (input && typeof input.name != 'undefined' && typeof input.value != 'undefined') {
|
||||||
return {
|
return {
|
||||||
name: input.name,
|
name: input.name,
|
||||||
|
@ -415,9 +413,9 @@ export class CoreQuestionHelperProvider {
|
||||||
* @return {string} Validation error message if present.
|
* @return {string} Validation error message if present.
|
||||||
*/
|
*/
|
||||||
getValidationErrorFromHtml(html: string): string {
|
getValidationErrorFromHtml(html: string): string {
|
||||||
this.div.innerHTML = html;
|
const element = this.domUtils.convertToElement(html);
|
||||||
|
|
||||||
return this.domUtils.getContentsOfElement(this.div, '.validationerror');
|
return this.domUtils.getContentsOfElement(element, '.validationerror');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -443,8 +441,8 @@ export class CoreQuestionHelperProvider {
|
||||||
* @param {any} question Question.
|
* @param {any} question Question.
|
||||||
*/
|
*/
|
||||||
loadLocalAnswersInHtml(question: any): void {
|
loadLocalAnswersInHtml(question: any): void {
|
||||||
const form = document.createElement('form');
|
const element = this.domUtils.convertToElement('<form>' + question.html + '</form>'),
|
||||||
form.innerHTML = question.html;
|
form = <HTMLFormElement> element.children[0];
|
||||||
|
|
||||||
// Search all input elements.
|
// Search all input elements.
|
||||||
Array.from(form.elements).forEach((element: HTMLInputElement | HTMLButtonElement) => {
|
Array.from(form.elements).forEach((element: HTMLInputElement | HTMLButtonElement) => {
|
||||||
|
@ -574,9 +572,9 @@ export class CoreQuestionHelperProvider {
|
||||||
* @return {boolean} Whether the button is found.
|
* @return {boolean} Whether the button is found.
|
||||||
*/
|
*/
|
||||||
protected searchBehaviourButton(question: any, htmlProperty: string, selector: string): boolean {
|
protected searchBehaviourButton(question: any, htmlProperty: string, selector: string): boolean {
|
||||||
this.div.innerHTML = question[htmlProperty];
|
const element = this.domUtils.convertToElement(question[htmlProperty]);
|
||||||
|
|
||||||
const button = <HTMLInputElement> this.div.querySelector(selector);
|
const button = <HTMLInputElement> element.querySelector(selector);
|
||||||
if (button) {
|
if (button) {
|
||||||
// Add a behaviour button to the question's "behaviourButtons" property.
|
// Add a behaviour button to the question's "behaviourButtons" property.
|
||||||
this.addBehaviourButton(question, button);
|
this.addBehaviourButton(question, button);
|
||||||
|
@ -585,7 +583,7 @@ export class CoreQuestionHelperProvider {
|
||||||
button.parentElement.removeChild(button);
|
button.parentElement.removeChild(button);
|
||||||
|
|
||||||
// Update the question's html.
|
// Update the question's html.
|
||||||
question[htmlProperty] = this.div.innerHTML;
|
question[htmlProperty] = element.innerHTML;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,8 @@ export class CoreDomUtilsProvider {
|
||||||
'search', 'tel', 'text', 'time', 'url', 'week'];
|
'search', 'tel', 'text', 'time', 'url', 'week'];
|
||||||
protected INSTANCE_ID_ATTR_NAME = 'core-instance-id';
|
protected INSTANCE_ID_ATTR_NAME = 'core-instance-id';
|
||||||
|
|
||||||
protected element = document.createElement('div'); // Fake element to use in some functions, to prevent creating it each time.
|
protected parser = new DOMParser(); // Parser to treat HTML.
|
||||||
|
|
||||||
protected matchesFn: string; // Name of the "matches" function to use when simulating a closest call.
|
protected matchesFn: string; // Name of the "matches" function to use when simulating a closest call.
|
||||||
protected instances: {[id: string]: any} = {}; // Store component/directive instances by id.
|
protected instances: {[id: string]: any} = {}; // Store component/directive instances by id.
|
||||||
protected lastInstanceId = 0;
|
protected lastInstanceId = 0;
|
||||||
|
@ -124,6 +125,28 @@ export class CoreDomUtilsProvider {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert some HTML as text into an HTMLElement. This HTML is put inside a div or a body.
|
||||||
|
*
|
||||||
|
* @param {string} html Text to convert.
|
||||||
|
* @return {HTMLElement} Element.
|
||||||
|
*/
|
||||||
|
convertToElement(html: string): HTMLElement {
|
||||||
|
if (this.parser) {
|
||||||
|
const doc = this.parser.parseFromString(html, 'text/html');
|
||||||
|
|
||||||
|
// Verify that the doc is valid. In some OS like Android 4.4 only XML parsing is supported, so doc is null.
|
||||||
|
if (doc) {
|
||||||
|
return doc.body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const element = document.createElement('div');
|
||||||
|
element.innerHTML = html;
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a "cancelled" error. These errors won't display an error message in showErrorModal functions.
|
* Create a "cancelled" error. These errors won't display an error message in showErrorModal functions.
|
||||||
*
|
*
|
||||||
|
@ -169,8 +192,8 @@ export class CoreDomUtilsProvider {
|
||||||
const urls = [];
|
const urls = [];
|
||||||
let elements;
|
let elements;
|
||||||
|
|
||||||
this.element.innerHTML = html;
|
const element = this.convertToElement(html);
|
||||||
elements = this.element.querySelectorAll('a, img, audio, video, source, track');
|
elements = element.querySelectorAll('a, img, audio, video, source, track');
|
||||||
|
|
||||||
for (let i = 0; i < elements.length; i++) {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
const element = elements[i];
|
const element = elements[i];
|
||||||
|
@ -599,21 +622,21 @@ export class CoreDomUtilsProvider {
|
||||||
removeElementFromHtml(html: string, selector: string, removeAll?: boolean): string {
|
removeElementFromHtml(html: string, selector: string, removeAll?: boolean): string {
|
||||||
let selected;
|
let selected;
|
||||||
|
|
||||||
this.element.innerHTML = html;
|
const element = this.convertToElement(html);
|
||||||
|
|
||||||
if (removeAll) {
|
if (removeAll) {
|
||||||
selected = this.element.querySelectorAll(selector);
|
selected = element.querySelectorAll(selector);
|
||||||
for (let i = 0; i < selected.length; i++) {
|
for (let i = 0; i < selected.length; i++) {
|
||||||
selected[i].remove();
|
selected[i].remove();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selected = this.element.querySelector(selector);
|
selected = element.querySelector(selector);
|
||||||
if (selected) {
|
if (selected) {
|
||||||
selected.remove();
|
selected.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.element.innerHTML;
|
return element.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -665,10 +688,10 @@ export class CoreDomUtilsProvider {
|
||||||
let media,
|
let media,
|
||||||
anchors;
|
anchors;
|
||||||
|
|
||||||
this.element.innerHTML = html;
|
const element = this.convertToElement(html);
|
||||||
|
|
||||||
// Treat elements with src (img, audio, video, ...).
|
// Treat elements with src (img, audio, video, ...).
|
||||||
media = this.element.querySelectorAll('img, video, audio, source, track');
|
media = element.querySelectorAll('img, video, audio, source, track');
|
||||||
media.forEach((media: HTMLElement) => {
|
media.forEach((media: HTMLElement) => {
|
||||||
let newSrc = paths[this.textUtils.decodeURIComponent(media.getAttribute('src'))];
|
let newSrc = paths[this.textUtils.decodeURIComponent(media.getAttribute('src'))];
|
||||||
|
|
||||||
|
@ -686,7 +709,7 @@ export class CoreDomUtilsProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Now treat links.
|
// Now treat links.
|
||||||
anchors = this.element.querySelectorAll('a');
|
anchors = element.querySelectorAll('a');
|
||||||
anchors.forEach((anchor: HTMLElement) => {
|
anchors.forEach((anchor: HTMLElement) => {
|
||||||
const href = this.textUtils.decodeURIComponent(anchor.getAttribute('href')),
|
const href = this.textUtils.decodeURIComponent(anchor.getAttribute('href')),
|
||||||
newUrl = paths[href];
|
newUrl = paths[href];
|
||||||
|
@ -700,7 +723,7 @@ export class CoreDomUtilsProvider {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.element.innerHTML;
|
return element.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1104,13 +1127,13 @@ export class CoreDomUtilsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts HTML formatted text to DOM element.
|
* Converts HTML formatted text to DOM element(s).
|
||||||
* @param {string} text HTML text.
|
*
|
||||||
* @return {HTMLCollection} Same text converted to HTMLCollection.
|
* @param {string} text HTML text.
|
||||||
|
* @return {HTMLCollection} Same text converted to HTMLCollection.
|
||||||
*/
|
*/
|
||||||
toDom(text: string): HTMLCollection {
|
toDom(text: string): HTMLCollection {
|
||||||
const element = document.createElement('div');
|
const element = this.convertToElement(text);
|
||||||
element.innerHTML = text;
|
|
||||||
|
|
||||||
return element.children;
|
return element.children;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ export class CoreTextUtilsProvider {
|
||||||
{old: /_mmaModWorkshop/g, new: '_AddonModWorkshop'},
|
{old: /_mmaModWorkshop/g, new: '_AddonModWorkshop'},
|
||||||
];
|
];
|
||||||
|
|
||||||
protected element = document.createElement('div'); // Fake element to use in some functions, to prevent creating it each time.
|
protected parser = new DOMParser(); // Parser to treat HTML.
|
||||||
|
|
||||||
constructor(private translate: TranslateService, private langProvider: CoreLangProvider, private modalCtrl: ModalController) { }
|
constructor(private translate: TranslateService, private langProvider: CoreLangProvider, private modalCtrl: ModalController) { }
|
||||||
|
|
||||||
|
@ -142,8 +142,8 @@ export class CoreTextUtilsProvider {
|
||||||
// First, we use a regexpr.
|
// First, we use a regexpr.
|
||||||
text = text.replace(/(<([^>]+)>)/ig, '');
|
text = text.replace(/(<([^>]+)>)/ig, '');
|
||||||
// Then, we rely on the browser. We need to wrap the text to be sure is HTML.
|
// Then, we rely on the browser. We need to wrap the text to be sure is HTML.
|
||||||
this.element.innerHTML = text;
|
const element = this.convertToElement(text);
|
||||||
text = this.element.textContent;
|
text = element.textContent;
|
||||||
// Recover or remove new lines.
|
// Recover or remove new lines.
|
||||||
text = this.replaceNewLines(text, singleLine ? ' ' : '<br>');
|
text = this.replaceNewLines(text, singleLine ? ' ' : '<br>');
|
||||||
|
|
||||||
|
@ -176,6 +176,29 @@ export class CoreTextUtilsProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert some HTML as text into an HTMLElement. This HTML is put inside a div or a body.
|
||||||
|
* This function is the same as in DomUtils, but we cannot use that one because of circular dependencies.
|
||||||
|
*
|
||||||
|
* @param {string} html Text to convert.
|
||||||
|
* @return {HTMLElement} Element.
|
||||||
|
*/
|
||||||
|
protected convertToElement(html: string): HTMLElement {
|
||||||
|
if (this.parser) {
|
||||||
|
const doc = this.parser.parseFromString(html, 'text/html');
|
||||||
|
|
||||||
|
// Verify that the doc is valid. In some OS like Android 4.4 only XML parsing is supported, so doc is null.
|
||||||
|
if (doc) {
|
||||||
|
return doc.body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const element = document.createElement('div');
|
||||||
|
element.innerHTML = html;
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count words in a text.
|
* Count words in a text.
|
||||||
*
|
*
|
||||||
|
@ -225,9 +248,8 @@ export class CoreTextUtilsProvider {
|
||||||
*/
|
*/
|
||||||
decodeHTMLEntities(text: string): string {
|
decodeHTMLEntities(text: string): string {
|
||||||
if (text) {
|
if (text) {
|
||||||
this.element.innerHTML = text;
|
const element = this.convertToElement(text);
|
||||||
text = this.element.textContent;
|
text = element.textContent;
|
||||||
this.element.textContent = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
|
|
Loading…
Reference in New Issue