commit
						cc5c476019
					
				@ -39,10 +39,10 @@ import { CoreLogger } from '@singletons/logger';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent {
 | 
					export class AddonBlockTimelineComponent implements OnInit, ICoreBlockComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sort = new FormControl();
 | 
					    sort = new FormControl(AddonBlockTimelineSort.ByDates);
 | 
				
			||||||
    sort$!: Observable<AddonBlockTimelineSort>;
 | 
					    sort$!: Observable<AddonBlockTimelineSort>;
 | 
				
			||||||
    sortOptions!: AddonBlockTimelineOption<AddonBlockTimelineSort>[];
 | 
					    sortOptions!: AddonBlockTimelineOption<AddonBlockTimelineSort>[];
 | 
				
			||||||
    filter = new FormControl();
 | 
					    filter = new FormControl(AddonBlockTimelineFilter.Next30Days);
 | 
				
			||||||
    filter$!: Observable<AddonBlockTimelineFilter>;
 | 
					    filter$!: Observable<AddonBlockTimelineFilter>;
 | 
				
			||||||
    statusFilterOptions!: AddonBlockTimelineOption<AddonBlockTimelineFilter>[];
 | 
					    statusFilterOptions!: AddonBlockTimelineOption<AddonBlockTimelineFilter>[];
 | 
				
			||||||
    dateFilterOptions!: AddonBlockTimelineOption<AddonBlockTimelineFilter>[];
 | 
					    dateFilterOptions!: AddonBlockTimelineOption<AddonBlockTimelineFilter>[];
 | 
				
			||||||
 | 
				
			|||||||
@ -78,9 +78,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Form variables.
 | 
					    // Form variables.
 | 
				
			||||||
    form: FormGroup;
 | 
					    form: FormGroup;
 | 
				
			||||||
    typeControl: FormControl;
 | 
					    typeControl: FormControl<AddonCalendarEventType | null>;
 | 
				
			||||||
    groupControl: FormControl;
 | 
					    groupControl: FormControl<number | null>;
 | 
				
			||||||
    descriptionControl: FormControl;
 | 
					    descriptionControl: FormControl<string>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Reminders.
 | 
					    // Reminders.
 | 
				
			||||||
    remindersEnabled = false;
 | 
					    remindersEnabled = false;
 | 
				
			||||||
@ -103,9 +103,9 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
				
			|||||||
        this.form = new FormGroup({});
 | 
					        this.form = new FormGroup({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Initialize form variables.
 | 
					        // Initialize form variables.
 | 
				
			||||||
        this.typeControl = this.fb.control('', Validators.required);
 | 
					        this.typeControl = this.fb.control(null, Validators.required);
 | 
				
			||||||
        this.groupControl = this.fb.control('');
 | 
					        this.groupControl = this.fb.control(null);
 | 
				
			||||||
        this.descriptionControl = this.fb.control('');
 | 
					        this.descriptionControl = this.fb.control('', { nonNullable: true });
 | 
				
			||||||
        this.form.addControl('name', this.fb.control('', Validators.required));
 | 
					        this.form.addControl('name', this.fb.control('', Validators.required));
 | 
				
			||||||
        this.form.addControl('eventtype', this.typeControl);
 | 
					        this.form.addControl('eventtype', this.typeControl);
 | 
				
			||||||
        this.form.addControl('categoryid', this.fb.control(''));
 | 
					        this.form.addControl('categoryid', this.fb.control(''));
 | 
				
			||||||
@ -322,11 +322,11 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.form.controls.name.setValue(event.name);
 | 
					        this.form.controls.name.setValue(event.name);
 | 
				
			||||||
        this.form.controls.timestart.setValue(CoreTimeUtils.toDatetimeFormat(event.timestart * 1000));
 | 
					        this.form.controls.timestart.setValue(CoreTimeUtils.toDatetimeFormat(event.timestart * 1000));
 | 
				
			||||||
        this.form.controls.eventtype.setValue(event.eventtype);
 | 
					        this.typeControl.setValue(event.eventtype as AddonCalendarEventType);
 | 
				
			||||||
        this.form.controls.categoryid.setValue(event.categoryid || '');
 | 
					        this.form.controls.categoryid.setValue(event.categoryid || '');
 | 
				
			||||||
        this.form.controls.courseid.setValue(courseId || '');
 | 
					        this.form.controls.courseid.setValue(courseId || '');
 | 
				
			||||||
        this.form.controls.groupcourseid.setValue(courseId || '');
 | 
					        this.form.controls.groupcourseid.setValue(courseId || '');
 | 
				
			||||||
        this.form.controls.groupid.setValue(event.groupid || '');
 | 
					        this.groupControl.setValue(event.groupid || null);
 | 
				
			||||||
        this.form.controls.description.setValue(event.description);
 | 
					        this.form.controls.description.setValue(event.description);
 | 
				
			||||||
        this.form.controls.location.setValue(event.location);
 | 
					        this.form.controls.location.setValue(event.location);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -410,7 +410,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            await this.loadGroups(courseId);
 | 
					            await this.loadGroups(courseId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.groupControl.setValue('');
 | 
					            this.groupControl.setValue(null);
 | 
				
			||||||
        } catch (error) {
 | 
					        } catch (error) {
 | 
				
			||||||
            CoreDomUtils.showErrorModalDefault(error, 'Error getting data.');
 | 
					            CoreDomUtils.showErrorModalDefault(error, 'Error getting data.');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,7 @@ import { AddonModAssignFeedbackPluginBaseComponent } from '@addons/mod/assign/cl
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedbackPluginBaseComponent implements OnInit {
 | 
					export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedbackPluginBaseComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    control?: FormControl;
 | 
					    control?: FormControl<string>;
 | 
				
			||||||
    component = AddonModAssignProvider.COMPONENT;
 | 
					    component = AddonModAssignProvider.COMPONENT;
 | 
				
			||||||
    text = '';
 | 
					    text = '';
 | 
				
			||||||
    isSent = false;
 | 
					    isSent = false;
 | 
				
			||||||
@ -76,7 +76,7 @@ export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedb
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            } else if (this.edit) {
 | 
					            } else if (this.edit) {
 | 
				
			||||||
                this.control = this.fb.control(this.text);
 | 
					                this.control = this.fb.control(this.text, { nonNullable: true });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
            this.loaded = true;
 | 
					            this.loaded = true;
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ import { AddonModAssignSubmissionOnlineTextPluginData } from '../services/handle
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignSubmissionPluginBaseComponent implements OnInit {
 | 
					export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignSubmissionPluginBaseComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    control?: FormControl;
 | 
					    control?: FormControl<string>;
 | 
				
			||||||
    words = 0;
 | 
					    words = 0;
 | 
				
			||||||
    component = AddonModAssignProvider.COMPONENT;
 | 
					    component = AddonModAssignProvider.COMPONENT;
 | 
				
			||||||
    text = '';
 | 
					    text = '';
 | 
				
			||||||
@ -94,7 +94,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
 | 
				
			|||||||
                });
 | 
					                });
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // Create and add the control.
 | 
					                // Create and add the control.
 | 
				
			||||||
                this.control = this.fb.control(this.text);
 | 
					                this.control = this.fb.control(this.text, { nonNullable: true });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Calculate initial words.
 | 
					            // Calculate initial words.
 | 
				
			||||||
 | 
				
			|||||||
@ -83,7 +83,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @ViewChild('replyFormEl') formElement!: ElementRef;
 | 
					    @ViewChild('replyFormEl') formElement!: ElementRef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    messageControl = new FormControl();
 | 
					    messageControl = new FormControl<string | null>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    uniqueId!: string;
 | 
					    uniqueId!: string;
 | 
				
			||||||
    defaultReplySubject!: string;
 | 
					    defaultReplySubject!: string;
 | 
				
			||||||
 | 
				
			|||||||
@ -70,7 +70,7 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea
 | 
				
			|||||||
    @ViewChild(CoreEditorRichTextEditorComponent) messageEditor!: CoreEditorRichTextEditorComponent;
 | 
					    @ViewChild(CoreEditorRichTextEditorComponent) messageEditor!: CoreEditorRichTextEditorComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    component = AddonModForumProvider.COMPONENT;
 | 
					    component = AddonModForumProvider.COMPONENT;
 | 
				
			||||||
    messageControl = new FormControl();
 | 
					    messageControl = new FormControl<string | null>(null);
 | 
				
			||||||
    groupsLoaded = false;
 | 
					    groupsLoaded = false;
 | 
				
			||||||
    showGroups = false;
 | 
					    showGroups = false;
 | 
				
			||||||
    hasOffline = false;
 | 
					    hasOffline = false;
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,7 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave {
 | 
				
			|||||||
    courseId!: number;
 | 
					    courseId!: number;
 | 
				
			||||||
    loaded = false;
 | 
					    loaded = false;
 | 
				
			||||||
    glossary?: AddonModGlossaryGlossary;
 | 
					    glossary?: AddonModGlossaryGlossary;
 | 
				
			||||||
    definitionControl = new FormControl();
 | 
					    definitionControl = new FormControl<string | null>(null);
 | 
				
			||||||
    categories: AddonModGlossaryCategory[] = [];
 | 
					    categories: AddonModGlossaryCategory[] = [];
 | 
				
			||||||
    showAliases = true;
 | 
					    showAliases = true;
 | 
				
			||||||
    editorExtraParams: Record<string, unknown> = {};
 | 
					    editorExtraParams: Record<string, unknown> = {};
 | 
				
			||||||
 | 
				
			|||||||
@ -369,7 +369,7 @@ export class AddonModLessonHelperProvider {
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Init the control.
 | 
					            // Init the control.
 | 
				
			||||||
            essayQuestion.control = this.formBuilder.control('');
 | 
					            essayQuestion.control = this.formBuilder.control('', { nonNullable: true });
 | 
				
			||||||
            questionForm.addControl(essayQuestion.textarea.name, essayQuestion.control);
 | 
					            questionForm.addControl(essayQuestion.textarea.name, essayQuestion.control);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -635,7 +635,7 @@ export type AddonModLessonInputQuestion = AddonModLessonQuestionBasicData & {
 | 
				
			|||||||
export type AddonModLessonEssayQuestion = AddonModLessonQuestionBasicData & {
 | 
					export type AddonModLessonEssayQuestion = AddonModLessonQuestionBasicData & {
 | 
				
			||||||
    useranswer?: string; // User answer, for reviewing.
 | 
					    useranswer?: string; // User answer, for reviewing.
 | 
				
			||||||
    textarea?: AddonModLessonTextareaData; // Data for the textarea.
 | 
					    textarea?: AddonModLessonTextareaData; // Data for the textarea.
 | 
				
			||||||
    control?: FormControl; // Form control.
 | 
					    control?: FormControl<string>; // Form control.
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 | 
				
			|||||||
@ -157,14 +157,14 @@ export class AddonModUrlProvider {
 | 
				
			|||||||
        url = url || '';
 | 
					        url = url || '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const matches = url.match(/\//g);
 | 
					        const matches = url.match(/\//g);
 | 
				
			||||||
        const extension = CoreMimetypeUtils.getFileExtension(url);
 | 
					        const extension = CoreMimetypeUtils.guessExtensionFromUrl(url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!matches || matches.length < 3 || url.slice(-1) === '/' || extension == 'php') {
 | 
					        if (!matches || matches.length < 3 || url.slice(-1) === '/' || extension == 'php') {
 | 
				
			||||||
            // Use default icon.
 | 
					            // Use default icon.
 | 
				
			||||||
            return '';
 | 
					            return '';
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const icon = CoreMimetypeUtils.getFileIcon(url);
 | 
					        const icon = CoreMimetypeUtils.getExtensionIcon(extension ?? '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // We do not want to return those icon types, the module icon is more appropriate.
 | 
					        // We do not want to return those icon types, the module icon is more appropriate.
 | 
				
			||||||
        if (icon === CoreMimetypeUtils.getFileIconForType('unknown') ||
 | 
					        if (icon === CoreMimetypeUtils.getFileIconForType('unknown') ||
 | 
				
			||||||
 | 
				
			|||||||
@ -47,7 +47,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave {
 | 
				
			|||||||
    courseId?: number; // Course the wiki belongs to.
 | 
					    courseId?: number; // Course the wiki belongs to.
 | 
				
			||||||
    title?: string; // Title to display.
 | 
					    title?: string; // Title to display.
 | 
				
			||||||
    pageForm: FormGroup; // The form group.
 | 
					    pageForm: FormGroup; // The form group.
 | 
				
			||||||
    contentControl: FormControl; // The FormControl for the page content.
 | 
					    contentControl: FormControl<string>; // The FormControl for the page content.
 | 
				
			||||||
    canEditTitle = false; // Whether title can be edited.
 | 
					    canEditTitle = false; // Whether title can be edited.
 | 
				
			||||||
    loaded = false; // Whether the data has been loaded.
 | 
					    loaded = false; // Whether the data has been loaded.
 | 
				
			||||||
    component = AddonModWikiProvider.COMPONENT; // Component to link the files to.
 | 
					    component = AddonModWikiProvider.COMPONENT; // Component to link the files to.
 | 
				
			||||||
@ -74,7 +74,7 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy, CanLeave {
 | 
				
			|||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        protected formBuilder: FormBuilder,
 | 
					        protected formBuilder: FormBuilder,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        this.contentControl = this.formBuilder.control('');
 | 
					        this.contentControl = this.formBuilder.control('', { nonNullable: true });
 | 
				
			||||||
        this.pageForm = this.formBuilder.group({});
 | 
					        this.pageForm = this.formBuilder.group({});
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -73,7 +73,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe
 | 
				
			|||||||
    assessmentStrategyLoaded = false;
 | 
					    assessmentStrategyLoaded = false;
 | 
				
			||||||
    notSupported = false;
 | 
					    notSupported = false;
 | 
				
			||||||
    feedbackText = '';
 | 
					    feedbackText = '';
 | 
				
			||||||
    feedbackControl = new FormControl();
 | 
					    feedbackControl = new FormControl<string | null>(null);
 | 
				
			||||||
    overallFeedkback = false;
 | 
					    overallFeedkback = false;
 | 
				
			||||||
    overallFeedkbackRequired = false;
 | 
					    overallFeedkbackRequired = false;
 | 
				
			||||||
    component = ADDON_MOD_WORKSHOP_COMPONENT;
 | 
					    component = ADDON_MOD_WORKSHOP_COMPONENT;
 | 
				
			||||||
 | 
				
			|||||||
@ -32,7 +32,7 @@ import { CoreFileEntry } from '@services/file-helper';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent<AddonModQuizEssayQuestion> {
 | 
					export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent<AddonModQuizEssayQuestion> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    formControl?: FormControl;
 | 
					    formControl?: FormControl<string | null>;
 | 
				
			||||||
    attachments?: CoreFileEntry[];
 | 
					    attachments?: CoreFileEntry[];
 | 
				
			||||||
    uploadFilesSupported = false;
 | 
					    uploadFilesSupported = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -52,7 +52,7 @@ export class AddonQtypeEssayComponent extends CoreQuestionBaseComponent<AddonMod
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.initEssayComponent(this.review);
 | 
					        this.initEssayComponent(this.review);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.formControl = this.fb.control(this.question?.textarea?.text);
 | 
					        this.formControl = this.fb.control(this.question?.textarea?.text ?? null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.question?.allowsAttachments && this.uploadFilesSupported && !this.review) {
 | 
					        if (this.question?.allowsAttachments && this.uploadFilesSupported && !this.review) {
 | 
				
			||||||
            this.loadAttachments();
 | 
					            this.loadAttachments();
 | 
				
			||||||
 | 
				
			|||||||
@ -27,20 +27,23 @@ import { CoreUtils } from '@services/utils/utils';
 | 
				
			|||||||
    templateUrl: 'addon-user-profile-field-checkbox.html',
 | 
					    templateUrl: 'addon-user-profile-field-checkbox.html',
 | 
				
			||||||
    styleUrls: ['./checkbox.scss'],
 | 
					    styleUrls: ['./checkbox.scss'],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AddonUserProfileFieldCheckboxComponent extends CoreUserProfileFieldBaseComponent {
 | 
					export class AddonUserProfileFieldCheckboxComponent extends CoreUserProfileFieldBaseComponent<boolean> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Create the Form control.
 | 
					     * Create the Form control.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @returns Form control.
 | 
					     * @returns Form control.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected createFormControl(field: AuthEmailSignupProfileField): FormControl {
 | 
					    protected createFormControl(field: AuthEmailSignupProfileField): FormControl<boolean> {
 | 
				
			||||||
        const formData = {
 | 
					        const formData = {
 | 
				
			||||||
            value: CoreUtils.isTrueOrOne(field.defaultdata),
 | 
					            value: CoreUtils.isTrueOrOne(field.defaultdata),
 | 
				
			||||||
            disabled: this.disabled,
 | 
					            disabled: this.disabled,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return new FormControl(formData, this.required && !field.locked ? Validators.requiredTrue : null);
 | 
					        return new FormControl(formData, {
 | 
				
			||||||
 | 
					            validators: this.required && !field.locked ? Validators.requiredTrue : null,
 | 
				
			||||||
 | 
					            nonNullable: true,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -28,7 +28,7 @@ import { CoreUserProfileFieldBaseComponent } from '@features/user/classes/base-p
 | 
				
			|||||||
    selector: 'addon-user-profile-field-datetime',
 | 
					    selector: 'addon-user-profile-field-datetime',
 | 
				
			||||||
    templateUrl: 'addon-user-profile-field-datetime.html',
 | 
					    templateUrl: 'addon-user-profile-field-datetime.html',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileFieldBaseComponent {
 | 
					export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileFieldBaseComponent<string | undefined> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ionDateTimePresentation = 'date';
 | 
					    ionDateTimePresentation = 'date';
 | 
				
			||||||
    min?: string;
 | 
					    min?: string;
 | 
				
			||||||
@ -84,13 +84,16 @@ export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileField
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @returns Form control.
 | 
					     * @returns Form control.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected createFormControl(field: AuthEmailSignupProfileField): FormControl {
 | 
					    protected createFormControl(field: AuthEmailSignupProfileField): FormControl<string | undefined> {
 | 
				
			||||||
        const formData = {
 | 
					        const formData = {
 | 
				
			||||||
            value: field.defaultdata != '0' ? field.defaultdata : undefined,
 | 
					            value: field.defaultdata != '0' ? field.defaultdata : undefined,
 | 
				
			||||||
            disabled: this.disabled,
 | 
					            disabled: this.disabled,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return new FormControl(formData, this.required && !field.locked ? Validators.required : null);
 | 
					        return new FormControl(formData, {
 | 
				
			||||||
 | 
					            validators: this.required && !field.locked ? Validators.required : null,
 | 
				
			||||||
 | 
					            nonNullable: true,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -11,9 +11,17 @@
 | 
				
			|||||||
        </ion-button>
 | 
					        </ion-button>
 | 
				
			||||||
    </core-navbar-buttons>
 | 
					    </core-navbar-buttons>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- allowfullscreen cannot be set dynamically using attribute binding, define 2 iframe elements. -->
 | 
				
			||||||
 | 
					    <ng-container *ngIf="allowFullscreen">
 | 
				
			||||||
        <iframe #iframe class="core-iframe" [attr.id]="id" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}" [src]="safeUrl"
 | 
					        <iframe #iframe class="core-iframe" [attr.id]="id" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}" [src]="safeUrl"
 | 
				
			||||||
        [attr.allowfullscreen]="allowFullscreen ? 'allowfullscreen' : null" [class.core-iframe-loading]="loading">
 | 
					            allowfullscreen="true" [class.core-iframe-loading]="loading">
 | 
				
			||||||
        </iframe>
 | 
					        </iframe>
 | 
				
			||||||
 | 
					    </ng-container>
 | 
				
			||||||
 | 
					    <ng-container *ngIf="!allowFullscreen">
 | 
				
			||||||
 | 
					        <iframe #iframe class="core-iframe" [attr.id]="id" [ngStyle]="{'width': iframeWidth, 'height': iframeHeight}" [src]="safeUrl"
 | 
				
			||||||
 | 
					            [class.core-iframe-loading]="loading">
 | 
				
			||||||
 | 
					        </iframe>
 | 
				
			||||||
 | 
					    </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <ion-button *ngIf="!loading && displayHelp" expand="block" fill="clear" (click)="openIframeHelpModal()" aria-haspopup="dialog"
 | 
					    <ion-button *ngIf="!loading && displayHelp" expand="block" fill="clear" (click)="openIframeHelpModal()" aria-haspopup="dialog"
 | 
				
			||||||
        class="core-button-as-link core-iframe-help">
 | 
					        class="core-button-as-link core-iframe-help">
 | 
				
			||||||
 | 
				
			|||||||
@ -85,12 +85,6 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.initialized = true;
 | 
					        this.initialized = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.iframeWidth = (this.iframeWidth && CoreDomUtils.formatPixelsSize(this.iframeWidth)) || '100%';
 | 
					 | 
				
			||||||
        this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%';
 | 
					 | 
				
			||||||
        this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen);
 | 
					 | 
				
			||||||
        this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar);
 | 
					 | 
				
			||||||
        this.autoFullscreenOnRotate = CoreUtils.isTrueOrOne(this.autoFullscreenOnRotate);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (this.showFullscreenOnToolbar || this.autoFullscreenOnRotate) {
 | 
					        if (this.showFullscreenOnToolbar || this.autoFullscreenOnRotate) {
 | 
				
			||||||
            // Leave fullscreen when navigating.
 | 
					            // Leave fullscreen when navigating.
 | 
				
			||||||
            this.navSubscription = Router.events
 | 
					            this.navSubscription = Router.events
 | 
				
			||||||
@ -157,6 +151,22 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
 | 
				
			|||||||
     * Detect changes on input properties.
 | 
					     * Detect changes on input properties.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    async ngOnChanges(changes: {[name: string]: SimpleChange }): Promise<void> {
 | 
					    async ngOnChanges(changes: {[name: string]: SimpleChange }): Promise<void> {
 | 
				
			||||||
 | 
					        if (changes.iframeWidth) {
 | 
				
			||||||
 | 
					            this.iframeWidth = (this.iframeWidth && CoreDomUtils.formatPixelsSize(this.iframeWidth)) || '100%';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (changes.iframeHeight) {
 | 
				
			||||||
 | 
					            this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (changes.allowFullscreen) {
 | 
				
			||||||
 | 
					            this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (changes.showFullscreenOnToolbar) {
 | 
				
			||||||
 | 
					            this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (changes.autoFullscreenOnRotate) {
 | 
				
			||||||
 | 
					            this.autoFullscreenOnRotate = CoreUtils.isTrueOrOne(this.autoFullscreenOnRotate);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!changes.src) {
 | 
					        if (!changes.src) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -40,7 +40,7 @@ import { FormControl } from '@angular/forms';
 | 
				
			|||||||
})
 | 
					})
 | 
				
			||||||
export class CoreInputErrorsComponent implements OnInit, OnChanges {
 | 
					export class CoreInputErrorsComponent implements OnInit, OnChanges {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Input() control?: FormControl; // Needed to be able to check the validity of the input.
 | 
					    @Input() control?: FormControl<unknown>; // Needed to be able to check the validity of the input.
 | 
				
			||||||
    @Input() errorMessages: Record<string, string> = {}; // Error messages to show. Keys must be the name of the error.
 | 
					    @Input() errorMessages: Record<string, string> = {}; // Error messages to show. Keys must be the name of the error.
 | 
				
			||||||
    @Input() errorText = ''; // Set other non automatic errors.
 | 
					    @Input() errorText = ''; // Set other non automatic errors.
 | 
				
			||||||
    errorKeys: string[] = [];
 | 
					    errorKeys: string[] = [];
 | 
				
			||||||
 | 
				
			|||||||
@ -47,8 +47,8 @@ export class CoreSitesListComponent<T extends CoreSiteBasicInfo> {
 | 
				
			|||||||
    @Input() currentSiteClickable?: boolean; // If set, specify a different clickable value for current site.
 | 
					    @Input() currentSiteClickable?: boolean; // If set, specify a different clickable value for current site.
 | 
				
			||||||
    @Output() onSiteClicked = new EventEmitter<T>();
 | 
					    @Output() onSiteClicked = new EventEmitter<T>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ContentChild('siteItem') siteItemTemplate?: TemplateRef<unknown>;
 | 
					    @ContentChild('siteItem') siteItemTemplate?: TemplateRef<{site: T; isCurrentSite: boolean}>;
 | 
				
			||||||
    @ContentChild('siteLabel') siteLabelTemplate?: TemplateRef<unknown>;
 | 
					    @ContentChild('siteLabel') siteLabelTemplate?: TemplateRef<{site: T; isCurrentSite: boolean}>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Check whether a site is clickable.
 | 
					     * Check whether a site is clickable.
 | 
				
			||||||
 | 
				
			|||||||
@ -67,7 +67,7 @@ export class CoreSwipeSlidesComponent<Item = unknown> implements OnChanges, OnDe
 | 
				
			|||||||
        }, 0);
 | 
					        }, 0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ContentChild(TemplateRef) template?: TemplateRef<unknown>; // Template defined by the content.
 | 
					    @ContentChild(TemplateRef) template?: TemplateRef<{item: Item; active: boolean}>; // Template defined by the content.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected hostElement: HTMLElement;
 | 
					    protected hostElement: HTMLElement;
 | 
				
			||||||
    protected unsubscribe?: () => void;
 | 
					    protected unsubscribe?: () => void;
 | 
				
			||||||
 | 
				
			|||||||
@ -68,7 +68,7 @@ export class CoreTabComponent implements OnInit, OnDestroy, CoreTabBase {
 | 
				
			|||||||
    @Input() id = ''; // An ID to identify the tab.
 | 
					    @Input() id = ''; // An ID to identify the tab.
 | 
				
			||||||
    @Output() ionSelect: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>();
 | 
					    @Output() ionSelect: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ContentChild(TemplateRef) template?: TemplateRef<unknown>; // Template defined by the content.
 | 
					    @ContentChild(TemplateRef) template?: TemplateRef<void>; // Template defined by the content.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    element: HTMLElement; // The core-tab element.
 | 
					    element: HTMLElement; // The core-tab element.
 | 
				
			||||||
    loaded = false;
 | 
					    loaded = false;
 | 
				
			||||||
 | 
				
			|||||||
@ -66,7 +66,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
				
			|||||||
    // @todo Implement ControlValueAccessor https://angular.io/api/forms/ControlValueAccessor.
 | 
					    // @todo Implement ControlValueAccessor https://angular.io/api/forms/ControlValueAccessor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Input() placeholder = ''; // Placeholder to set in textarea.
 | 
					    @Input() placeholder = ''; // Placeholder to set in textarea.
 | 
				
			||||||
    @Input() control?: FormControl; // Form control.
 | 
					    @Input() control?: FormControl<string | undefined | null>; // Form control.
 | 
				
			||||||
    @Input() name = 'core-rich-text-editor'; // Name to set to the textarea.
 | 
					    @Input() name = 'core-rich-text-editor'; // Name to set to the textarea.
 | 
				
			||||||
    @Input() component?: string; // The component to link the files to.
 | 
					    @Input() component?: string; // The component to link the files to.
 | 
				
			||||||
    @Input() componentId?: number; // An ID to use in conjunction with the component.
 | 
					    @Input() componentId?: number; // An ID to use in conjunction with the component.
 | 
				
			||||||
@ -75,7 +75,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
				
			|||||||
    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
					    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
				
			||||||
    @Input() elementId?: string; // An ID to set to the element.
 | 
					    @Input() elementId?: string; // An ID to set to the element.
 | 
				
			||||||
    @Input() draftExtraParams?: Record<string, unknown>; // Extra params to identify the draft.
 | 
					    @Input() draftExtraParams?: Record<string, unknown>; // Extra params to identify the draft.
 | 
				
			||||||
    @Output() contentChanged: EventEmitter<string>;
 | 
					    @Output() contentChanged: EventEmitter<string | undefined | null>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @ViewChild('editor') editor?: ElementRef; // WYSIWYG editor.
 | 
					    @ViewChild('editor') editor?: ElementRef; // WYSIWYG editor.
 | 
				
			||||||
    @ViewChild('textarea') textarea?: IonTextarea; // Textarea editor.
 | 
					    @ViewChild('textarea') textarea?: IonTextarea; // Textarea editor.
 | 
				
			||||||
@ -190,8 +190,8 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
				
			|||||||
        // Setup the editor.
 | 
					        // Setup the editor.
 | 
				
			||||||
        this.editorElement = this.editor?.nativeElement as HTMLDivElement;
 | 
					        this.editorElement = this.editor?.nativeElement as HTMLDivElement;
 | 
				
			||||||
        this.setContent(this.control?.value);
 | 
					        this.setContent(this.control?.value);
 | 
				
			||||||
        this.originalContent = this.control?.value;
 | 
					        this.originalContent = this.control?.value ?? undefined;
 | 
				
			||||||
        this.lastDraft = this.control?.value;
 | 
					        this.lastDraft = this.control?.value ?? '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Use paragraph on enter.
 | 
					        // Use paragraph on enter.
 | 
				
			||||||
        // eslint-disable-next-line deprecation/deprecation
 | 
					        // eslint-disable-next-line deprecation/deprecation
 | 
				
			||||||
@ -261,19 +261,19 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            // Apply the new content.
 | 
					            // Apply the new content.
 | 
				
			||||||
            this.setContent(newValue);
 | 
					            this.setContent(newValue);
 | 
				
			||||||
            this.originalContent = newValue;
 | 
					            this.originalContent = newValue ?? undefined;
 | 
				
			||||||
            this.infoMessage = undefined;
 | 
					            this.infoMessage = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Save a draft so the original content is saved.
 | 
					            // Save a draft so the original content is saved.
 | 
				
			||||||
            this.lastDraft = newValue;
 | 
					            this.lastDraft = newValue ?? '';
 | 
				
			||||||
            CoreEditorOffline.saveDraft(
 | 
					            CoreEditorOffline.saveDraft(
 | 
				
			||||||
                this.contextLevel || '',
 | 
					                this.contextLevel || '',
 | 
				
			||||||
                this.contextInstanceId || 0,
 | 
					                this.contextInstanceId || 0,
 | 
				
			||||||
                this.elementId || '',
 | 
					                this.elementId || '',
 | 
				
			||||||
                this.draftExtraParams || {},
 | 
					                this.draftExtraParams || {},
 | 
				
			||||||
                this.pageInstance,
 | 
					                this.pageInstance,
 | 
				
			||||||
                newValue,
 | 
					                this.lastDraft,
 | 
				
			||||||
                newValue,
 | 
					                this.originalContent,
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -579,7 +579,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
				
			|||||||
     * @param value text
 | 
					     * @param value text
 | 
				
			||||||
     * @returns If value is null only a white space.
 | 
					     * @returns If value is null only a white space.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected isNullOrWhiteSpace(value: string | null): boolean {
 | 
					    protected isNullOrWhiteSpace(value: string | null | undefined): boolean {
 | 
				
			||||||
        if (value == null || value === undefined) {
 | 
					        if (value == null || value === undefined) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -595,7 +595,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param value New content.
 | 
					     * @param value New content.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected setContent(value: string | null): void {
 | 
					    protected setContent(value: string | null | undefined): void {
 | 
				
			||||||
        if (!this.editorElement || !this.textarea) {
 | 
					        if (!this.editorElement || !this.textarea) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -974,7 +974,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const newText = this.control.value;
 | 
					            const newText = this.control.value ?? '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (this.lastDraft == newText) {
 | 
					            if (this.lastDraft == newText) {
 | 
				
			||||||
                // Text hasn't changed, nothing to save.
 | 
					                // Text hasn't changed, nothing to save.
 | 
				
			||||||
 | 
				
			|||||||
@ -69,8 +69,8 @@ export class CoreLoginEmailSignupPage implements OnInit {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Data for age verification.
 | 
					    // Data for age verification.
 | 
				
			||||||
    ageVerificationForm: FormGroup;
 | 
					    ageVerificationForm: FormGroup;
 | 
				
			||||||
    countryControl: FormControl;
 | 
					    countryControl: FormControl<string>;
 | 
				
			||||||
    signUpCountryControl?: FormControl;
 | 
					    signUpCountryControl?: FormControl<string>;
 | 
				
			||||||
    isMinor = false; // Whether the user is minor age.
 | 
					    isMinor = false; // Whether the user is minor age.
 | 
				
			||||||
    ageDigitalConsentVerification?: boolean; // Whether the age verification is enabled.
 | 
					    ageDigitalConsentVerification?: boolean; // Whether the age verification is enabled.
 | 
				
			||||||
    supportName?: string;
 | 
					    supportName?: string;
 | 
				
			||||||
@ -93,7 +93,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
 | 
				
			|||||||
        this.ageVerificationForm = this.fb.group({
 | 
					        this.ageVerificationForm = this.fb.group({
 | 
				
			||||||
            age: ['', Validators.required],
 | 
					            age: ['', Validators.required],
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        this.countryControl = this.fb.control('', Validators.required);
 | 
					        this.countryControl = this.fb.control('', { validators: Validators.required, nonNullable: true });
 | 
				
			||||||
        this.ageVerificationForm.addControl('country', this.countryControl);
 | 
					        this.ageVerificationForm.addControl('country', this.countryControl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create the signupForm with the basic controls. More controls will be added later.
 | 
					        // Create the signupForm with the basic controls. More controls will be added later.
 | 
				
			||||||
@ -141,7 +141,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    protected completeFormGroup(): void {
 | 
					    protected completeFormGroup(): void {
 | 
				
			||||||
        this.signupForm.addControl('city', this.fb.control(this.settings?.defaultcity || ''));
 | 
					        this.signupForm.addControl('city', this.fb.control(this.settings?.defaultcity || ''));
 | 
				
			||||||
        this.signUpCountryControl = this.fb.control(this.settings?.country || '');
 | 
					        this.signUpCountryControl = this.fb.control(this.settings?.country || '', { nonNullable: true });
 | 
				
			||||||
        this.signupForm.addControl('country', this.signUpCountryControl);
 | 
					        this.signupForm.addControl('country', this.signUpCountryControl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add the name fields.
 | 
					        // Add the name fields.
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ import { CoreUserProfileField } from '@features/user/services/user';
 | 
				
			|||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
    template: '',
 | 
					    template: '',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export abstract class CoreUserProfileFieldBaseComponent implements OnInit {
 | 
					export abstract class CoreUserProfileFieldBaseComponent<T = string> implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Input() field?: AuthEmailSignupProfileField | CoreUserProfileField; // The profile field to be rendered.
 | 
					    @Input() field?: AuthEmailSignupProfileField | CoreUserProfileField; // The profile field to be rendered.
 | 
				
			||||||
    @Input() signup = false; // True if editing the field in signup. Defaults to false.
 | 
					    @Input() signup = false; // True if editing the field in signup. Defaults to false.
 | 
				
			||||||
@ -36,7 +36,7 @@ export abstract class CoreUserProfileFieldBaseComponent implements OnInit {
 | 
				
			|||||||
    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
					    @Input() contextInstanceId?: number; // The instance ID related to the context.
 | 
				
			||||||
    @Input() courseId?: number; // Course ID the field belongs to (if any). It can be used to improve performance with filters.
 | 
					    @Input() courseId?: number; // Course ID the field belongs to (if any). It can be used to improve performance with filters.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    control?: FormControl;
 | 
					    control?: FormControl<T>;
 | 
				
			||||||
    modelName = '';
 | 
					    modelName = '';
 | 
				
			||||||
    value?: string;
 | 
					    value?: string;
 | 
				
			||||||
    required?: boolean;
 | 
					    required?: boolean;
 | 
				
			||||||
@ -91,13 +91,16 @@ export abstract class CoreUserProfileFieldBaseComponent implements OnInit {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @returns Form control.
 | 
					     * @returns Form control.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected createFormControl(field: AuthEmailSignupProfileField): FormControl {
 | 
					    protected createFormControl(field: AuthEmailSignupProfileField): FormControl<T> {
 | 
				
			||||||
        const formData = {
 | 
					        const formData = {
 | 
				
			||||||
            value: field.defaultdata,
 | 
					            value: (field.defaultdata ?? '') as T,
 | 
				
			||||||
            disabled: this.disabled,
 | 
					            disabled: this.disabled,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return new FormControl(formData, this.required && !field.locked ? Validators.required : null);
 | 
					        return new FormControl(formData, {
 | 
				
			||||||
 | 
					            validators: this.required && !field.locked ? Validators.required : null,
 | 
				
			||||||
 | 
					            nonNullable: true,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -312,17 +312,12 @@ export class CoreMimetypeUtilsProvider {
 | 
				
			|||||||
     * @returns The lowercased extension without the dot, or undefined.
 | 
					     * @returns The lowercased extension without the dot, or undefined.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    guessExtensionFromUrl(fileUrl: string): string | undefined {
 | 
					    guessExtensionFromUrl(fileUrl: string): string | undefined {
 | 
				
			||||||
        const split = CoreUrl.removeUrlAnchor(fileUrl).split('.');
 | 
					        const parsed = CoreUrl.parse(fileUrl);
 | 
				
			||||||
 | 
					        const split = parsed?.path?.split('.');
 | 
				
			||||||
        let extension: string | undefined;
 | 
					        let extension: string | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (split.length > 1) {
 | 
					        if (split && split.length > 1) {
 | 
				
			||||||
            let candidate = split[split.length - 1].toLowerCase();
 | 
					            const candidate = split[split.length - 1].toLowerCase();
 | 
				
			||||||
            // Remove params if any.
 | 
					 | 
				
			||||||
            const position = candidate.indexOf('?');
 | 
					 | 
				
			||||||
            if (position > -1) {
 | 
					 | 
				
			||||||
                candidate = candidate.substring(0, position);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (EXTENSION_REGEX.test(candidate)) {
 | 
					            if (EXTENSION_REGEX.test(candidate)) {
 | 
				
			||||||
                extension = candidate;
 | 
					                extension = candidate;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -22,10 +22,10 @@ import { catchError, filter } from 'rxjs/operators';
 | 
				
			|||||||
 * @param control Form control.
 | 
					 * @param control Form control.
 | 
				
			||||||
 * @returns Form control value observable.
 | 
					 * @returns Form control value observable.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function formControlValue<T = unknown>(control: FormControl): Observable<T> {
 | 
					export function formControlValue<T = unknown>(control: FormControl<T | null>): Observable<T> {
 | 
				
			||||||
    return control.valueChanges.pipe(
 | 
					    return control.valueChanges.pipe(
 | 
				
			||||||
        startWithOnSubscribed(() => control.value),
 | 
					        startWithOnSubscribed(() => control.value),
 | 
				
			||||||
        filter(value => value !== null),
 | 
					        filter((value): value is T => value !== null),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user