forked from CIT/Vmeda.Online
		
	
						commit
						dab503bb9e
					
				
							
								
								
									
										20
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -4319,11 +4319,11 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@ionic/angular": {
 | 
			
		||||
      "version": "5.9.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.9.2.tgz",
 | 
			
		||||
      "integrity": "sha512-5GzKg+l4au3xFECky2v/USlRsmTAXgvNO5Zalt7NUXc//VJIL2lQvswojE6FBWuM/xR5W0CWbJdFth19TaZWVQ==",
 | 
			
		||||
      "version": "5.9.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-5.9.4.tgz",
 | 
			
		||||
      "integrity": "sha512-U/85FePF48VaZXTudTwpVXDqhGmYfarl/7vki7a4umnIORnWtHqD2/pXsqqZ/O1EcbALwULYIeVXAfkFpPd2wQ==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@ionic/core": "5.9.2",
 | 
			
		||||
        "@ionic/core": "5.9.4",
 | 
			
		||||
        "tslib": "^1.9.3"
 | 
			
		||||
      },
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
@ -4666,9 +4666,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@ionic/core": {
 | 
			
		||||
      "version": "5.9.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.9.2.tgz",
 | 
			
		||||
      "integrity": "sha512-1ZqSBS8R6tGQsc+LsLxIRv0q3Ww6jwgJXLvdn6FmVWfpPbBvT+CjCuU9hqJ5qwM+atErblUMYSexvvpws8lGAA==",
 | 
			
		||||
      "version": "5.9.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.9.4.tgz",
 | 
			
		||||
      "integrity": "sha512-Ngz9yVT6fIiGdSxxBer8uJxP4w6PasvohYpLxhtMgYiWnyIu0vZra2ui3HrYukCzUo5/SbNPiUr1l7cj1E+7qw==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@stencil/core": "^2.4.0",
 | 
			
		||||
        "ionicons": "^5.5.3",
 | 
			
		||||
@ -5852,9 +5852,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@stencil/core": {
 | 
			
		||||
      "version": "2.11.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.11.0.tgz",
 | 
			
		||||
      "integrity": "sha512-/IubCWhVXCguyMUp/3zGrg3c882+RJNg/zpiKfyfJL3kRCOwe+/MD8OoAXVGdd+xAohZKIi1Ik+EHFlsptsjLg=="
 | 
			
		||||
      "version": "2.22.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.2.tgz",
 | 
			
		||||
      "integrity": "sha512-r+vbxsGNcBaV1VDOYW25lv4QfXTlNoIb5GpUX7rZ+cr59yqYCZC5tlV+IzX6YgHKW62ulCc9M3RYtTfHtNbNNw=="
 | 
			
		||||
    },
 | 
			
		||||
    "@storybook/addon-controls": {
 | 
			
		||||
      "version": "6.1.21",
 | 
			
		||||
 | 
			
		||||
@ -73,7 +73,7 @@
 | 
			
		||||
    "@ionic-native/status-bar": "5.36.0",
 | 
			
		||||
    "@ionic-native/web-intent": "5.36.0",
 | 
			
		||||
    "@ionic-native/zip": "5.36.0",
 | 
			
		||||
    "@ionic/angular": "5.9.2",
 | 
			
		||||
    "@ionic/angular": "5.9.4",
 | 
			
		||||
    "@moodlehq/cordova-plugin-file-opener": "3.0.5-moodle.1",
 | 
			
		||||
    "@moodlehq/cordova-plugin-file-transfer": "1.7.1-moodle.5",
 | 
			
		||||
    "@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3",
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@
 | 
			
		||||
        </ion-grid>
 | 
			
		||||
 | 
			
		||||
        <core-swipe-slides [manager]="manager">
 | 
			
		||||
            <ng-template let-month="item">
 | 
			
		||||
            <ng-template let-month="item" let-activeView="active">
 | 
			
		||||
                <!-- Calendar view. -->
 | 
			
		||||
                <ion-grid class="addon-calendar-months" role="table" aria-describedby="addon-calendar-monthname">
 | 
			
		||||
                    <div role="rowgroup">
 | 
			
		||||
@ -57,9 +57,9 @@
 | 
			
		||||
                                    "today": month.isCurrentMonth && day.istoday,
 | 
			
		||||
                                    "weekend": day.isweekend,
 | 
			
		||||
                                    "duration_finish": day.haslastdayofevent
 | 
			
		||||
                                }' [class.addon-calendar-event-past-day]="month.isPastMonth || day.ispast" role="cell" tabindex="0"
 | 
			
		||||
                                (ariaButtonClick)="dayClicked(day.mday)">
 | 
			
		||||
                                <p class="addon-calendar-day-number" role="button">
 | 
			
		||||
                                }' [class.addon-calendar-event-past-day]="month.isPastMonth || day.ispast" role="cell"
 | 
			
		||||
                                (ariaButtonClick)="dayClicked(day.mday)" [tabindex]="activeView ? 0 : -1">
 | 
			
		||||
                                <p class="addon-calendar-day-number">
 | 
			
		||||
                                    <span aria-hidden="true">{{ day.mday }}</span>
 | 
			
		||||
                                    <span class="sr-only">{{ day.periodName | translate }}</span>
 | 
			
		||||
                                </p>
 | 
			
		||||
@ -72,8 +72,8 @@
 | 
			
		||||
                                <div class="ion-hide-md-down addon-calendar-day-events" *ngIf="day.filteredEvents">
 | 
			
		||||
                                    <ng-container *ngFor="let event of day.filteredEvents | slice:0:4; let index = index">
 | 
			
		||||
                                        <div *ngIf="index < 3 || day.filteredEvents.length == 4" class="addon-calendar-event"
 | 
			
		||||
                                            [class.addon-calendar-event-past]="event.ispast" role="button" tabindex="0"
 | 
			
		||||
                                            (ariaButtonClick)="eventClicked(event, $event)">
 | 
			
		||||
                                            [class.addon-calendar-event-past]="event.ispast" (ariaButtonClick)="eventClicked(event, $event)"
 | 
			
		||||
                                            [tabindex]="activeView ? 0 : -1">
 | 
			
		||||
                                            <span class="calendar_event_type calendar_event_{{event.formattedType}}"></span>
 | 
			
		||||
                                            <ion-icon *ngIf="event.offline && !event.deleted" name="fas-clock"
 | 
			
		||||
                                                [attr.aria-label]="'core.notsent' | translate"></ion-icon>
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,6 @@
 | 
			
		||||
        @include border-end(1px, solid var(--addon-calendar-border-color));
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        min-height: 60px;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
 | 
			
		||||
        &:first-child {
 | 
			
		||||
            @include padding-horizontal(10px, null);
 | 
			
		||||
 | 
			
		||||
@ -74,26 +74,20 @@
 | 
			
		||||
                [lines]="discussion.groupname && 'none'" [attr.aria-current]="discussions?.getItemAriaCurrent(discussion)"
 | 
			
		||||
                (click)="discussions?.select(discussion)" button>
 | 
			
		||||
                <ion-label>
 | 
			
		||||
                    <div class="addon-mod-forum-discussion-title">
 | 
			
		||||
                        <p class="ion-text-wrap item-heading">
 | 
			
		||||
                            <ion-icon name="fas-map-pin" *ngIf="discussion.pinned"
 | 
			
		||||
                                [attr.aria-label]="'addon.mod_forum.discussionpinned' | translate"></ion-icon>
 | 
			
		||||
                            <ion-icon name="fas-star" class="addon-forum-star" *ngIf="!discussion.pinned && discussion.starred"
 | 
			
		||||
                                [attr.aria-label]="'addon.mod_forum.favourites' | translate"></ion-icon>
 | 
			
		||||
                            <core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module && module.id"
 | 
			
		||||
                                [courseId]="courseId">
 | 
			
		||||
                            </core-format-text>
 | 
			
		||||
                            <ion-icon name="fas-lock" *ngIf="discussion.locked" class="addon-mod-forum-locked-icon"
 | 
			
		||||
                                [attr.aria-label]="'addon.mod_forum.discussionlocked' | translate"></ion-icon>
 | 
			
		||||
                        </p>
 | 
			
		||||
                        <ion-button *ngIf="canPin || discussion.canlock || discussion.canfavourite" fill="clear"
 | 
			
		||||
                            [attr.aria-label]="('core.displayoptions' | translate)" (click)="showOptionsMenu($event, discussion)">
 | 
			
		||||
                            <ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true">
 | 
			
		||||
                            </ion-icon>
 | 
			
		||||
                        </ion-button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <p class="addon-mod-forum-discussion-title ion-text-wrap item-heading">
 | 
			
		||||
                        <ion-icon name="fas-map-pin" *ngIf="discussion.pinned"
 | 
			
		||||
                            [attr.aria-label]="'addon.mod_forum.discussionpinned' | translate"></ion-icon>
 | 
			
		||||
                        <ion-icon name="fas-star" class="addon-forum-star" *ngIf="!discussion.pinned && discussion.starred"
 | 
			
		||||
                            [attr.aria-label]="'addon.mod_forum.favourites' | translate"></ion-icon>
 | 
			
		||||
                        <core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module && module.id"
 | 
			
		||||
                            [courseId]="courseId">
 | 
			
		||||
                        </core-format-text>
 | 
			
		||||
                        <ion-icon name="fas-lock" *ngIf="discussion.locked" class="addon-mod-forum-locked-icon"
 | 
			
		||||
                            [attr.aria-label]="'addon.mod_forum.discussionlocked' | translate"></ion-icon>
 | 
			
		||||
                    </p>
 | 
			
		||||
                    <div class="addon-mod-forum-discussion-info">
 | 
			
		||||
                        <core-user-avatar *ngIf="discussion.userfullname" [user]="discussion" slot="start" [courseId]="courseId">
 | 
			
		||||
                        <core-user-avatar *ngIf="discussion.userfullname" [user]="discussion" slot="start" [courseId]="courseId"
 | 
			
		||||
                            [linkProfile]="false">
 | 
			
		||||
                        </core-user-avatar>
 | 
			
		||||
                        <div class="addon-mod-forum-discussion-author">
 | 
			
		||||
                            <span *ngIf="discussion.userfullname">{{discussion.userfullname}}</span>
 | 
			
		||||
@ -136,6 +130,11 @@
 | 
			
		||||
                        </ion-col>
 | 
			
		||||
                    </ion-row>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
                <ion-button *ngIf="canPin || discussion.canlock || discussion.canfavourite" fill="clear"
 | 
			
		||||
                    [attr.aria-label]="('core.displayoptions' | translate)" (click)="showOptionsMenu($event, discussion)" slot="end">
 | 
			
		||||
                    <ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true">
 | 
			
		||||
                    </ion-icon>
 | 
			
		||||
                </ion-button>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
 | 
			
		||||
            <core-infinite-loading [enabled]="discussions && discussions.loaded && !discussions.completed" [error]="fetchFailed"
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,6 @@
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .addon-mod-forum-discussion.item {
 | 
			
		||||
 | 
			
		||||
        ion-label {
 | 
			
		||||
            margin-top: 4px;
 | 
			
		||||
 | 
			
		||||
@ -35,21 +34,30 @@
 | 
			
		||||
            @include margin(0, 8px, 0, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .addon-mod-forum-discussion-title,
 | 
			
		||||
        .addon-mod-forum-discussion-info {
 | 
			
		||||
            display: flex;
 | 
			
		||||
            align-items: center;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .addon-mod-forum-discussion-title .item-heading,
 | 
			
		||||
        .addon-mod-forum-discussion-info .addon-mod-forum-discussion-author {
 | 
			
		||||
            flex-grow: 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .addon-mod-forum-discussion-title {
 | 
			
		||||
            @include margin-horizontal(null, 8px);
 | 
			
		||||
            line-height: 18px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        .addon-mod-forum-discussion-more-info.ios {
 | 
			
		||||
            font-size: 0.9rem;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ion-button {
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            @include position (4px, 8px, null, null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .core-group-selector {
 | 
			
		||||
 | 
			
		||||
@ -29,19 +29,19 @@ export class CoreAriaRoleTab<T = unknown> {
 | 
			
		||||
     * @param e Event.
 | 
			
		||||
     */
 | 
			
		||||
    keyDown(tabFindIndex: string, e: KeyboardEvent): void {
 | 
			
		||||
        if (e.key == ' ' ||
 | 
			
		||||
            e.key == 'Enter' ||
 | 
			
		||||
            e.key == 'Home' ||
 | 
			
		||||
            e.key == 'End' ||
 | 
			
		||||
            (this.isHorizontal() && (e.key == 'ArrowRight' || e.key == 'ArrowLeft')) ||
 | 
			
		||||
            (!this.isHorizontal() && (e.key == 'ArrowUp' ||e.key == 'ArrowDown'))
 | 
			
		||||
        if (e.key === ' ' ||
 | 
			
		||||
            e.key === 'Enter' ||
 | 
			
		||||
            e.key === 'Home' ||
 | 
			
		||||
            e.key === 'End' ||
 | 
			
		||||
            (this.isHorizontal() && (e.key === 'ArrowRight' || e.key === 'ArrowLeft')) ||
 | 
			
		||||
            (!this.isHorizontal() && (e.key === 'ArrowUp' ||e.key === 'ArrowDown'))
 | 
			
		||||
        ) {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            e.stopPropagation();
 | 
			
		||||
            e.stopImmediatePropagation();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (e.key == ' ' || e.key == 'Enter') {
 | 
			
		||||
        if (e.key === ' ' || e.key === 'Enter') {
 | 
			
		||||
            this.selectTabCandidate = tabFindIndex;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -64,7 +64,7 @@ export class CoreAriaRoleTab<T = unknown> {
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
        e.stopImmediatePropagation();
 | 
			
		||||
 | 
			
		||||
        if (e.key == ' ' || e.key == 'Enter') {
 | 
			
		||||
        if (e.key === ' ' || e.key === 'Enter') {
 | 
			
		||||
            if (this.selectTabCandidate === tabFindIndex) {
 | 
			
		||||
                this.selectTab(tabFindIndex, e);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -132,7 +132,7 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
 | 
			
		||||
     * @param event The mouse event.
 | 
			
		||||
     */
 | 
			
		||||
    doNotBlur(event: Event): void {
 | 
			
		||||
        if (event.type == 'keydown' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
 | 
			
		||||
        if (event.type === 'keydown' && !this.isValidKeyboardKey(<KeyboardEvent>event)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -147,7 +147,7 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
 | 
			
		||||
     * @returns Wether space or enter have been pressed.
 | 
			
		||||
     */
 | 
			
		||||
    protected isValidKeyboardKey(event: KeyboardEvent): boolean {
 | 
			
		||||
        return event.key == ' ' || event.key == 'Enter';
 | 
			
		||||
        return event.key === ' ' || event.key === 'Enter';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
<ion-slides *ngIf="loaded" (ionSlideWillChange)="slideWillChange()" (ionSlideDidChange)="slideDidChange()" [options]="options">
 | 
			
		||||
    <ion-slide *ngFor="let item of items; index as index">
 | 
			
		||||
    <ion-slide *ngFor="let item of items; index as index" [attr.aria-hidden]="!isActive(index)">
 | 
			
		||||
        <ng-container *ngIf="template" [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{item: item, active: isActive(index)}">
 | 
			
		||||
        </ng-container>
 | 
			
		||||
    </ion-slide>
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,14 @@
 | 
			
		||||
<img *ngIf="avatarUrl" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
 | 
			
		||||
    onError="this.src='assets/img/user-avatar.png'" (ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile"
 | 
			
		||||
    [attr.role]="linkProfile ? 'button' : null" [attr.tabindex]="linkProfile ? 0 : null" [class.clickable]="linkProfile">
 | 
			
		||||
<img *ngIf="avatarUrl && linkProfile" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
 | 
			
		||||
    onError="this.src='assets/img/user-avatar.png'" (ariaButtonClick)="gotoProfile($event)">
 | 
			
		||||
 | 
			
		||||
<img *ngIf="!avatarUrl" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
 | 
			
		||||
    (ariaButtonClick)="gotoProfile($event)" [attr.aria-hidden]="!linkProfile" [attr.role]="linkProfile ? 'button' : null"
 | 
			
		||||
    [attr.tabindex]="linkProfile ? 0 : null">
 | 
			
		||||
<img *ngIf="avatarUrl && !linkProfile" [src]="avatarUrl" [alt]="'core.pictureof' | translate:{$a: fullname}" core-external-content
 | 
			
		||||
    onError="this.src='assets/img/user-avatar.png'" aria-hidden="true">
 | 
			
		||||
 | 
			
		||||
<img *ngIf="!avatarUrl && linkProfile" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
 | 
			
		||||
    (ariaButtonClick)="gotoProfile($event)">
 | 
			
		||||
 | 
			
		||||
<img *ngIf="!avatarUrl && !linkProfile" src="assets/img/user-avatar.png" [alt]="'core.pictureof' | translate:{$a: fullname}"
 | 
			
		||||
    aria-hidden="true">
 | 
			
		||||
 | 
			
		||||
<span *ngIf="checkOnline && isOnline()" class="contact-status online" role="status" [attr.aria-label]="'core.online' | translate">
 | 
			
		||||
</span>
 | 
			
		||||
 | 
			
		||||
@ -5,9 +5,6 @@
 | 
			
		||||
    width: var(--core-avatar-size);
 | 
			
		||||
    height: var(--core-avatar-size);
 | 
			
		||||
 | 
			
		||||
    .clickable {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
    }
 | 
			
		||||
    img {
 | 
			
		||||
        border-radius: 50%;
 | 
			
		||||
        width: var(--core-avatar-size);
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Directive, ElementRef, OnInit, Output, EventEmitter } from '@angular/core';
 | 
			
		||||
import { Directive, ElementRef, OnInit, Output, EventEmitter, OnChanges, SimpleChanges, Input } from '@angular/core';
 | 
			
		||||
import { CoreDom } from '@singletons/dom';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -21,10 +21,11 @@ import { CoreDom } from '@singletons/dom';
 | 
			
		||||
@Directive({
 | 
			
		||||
    selector: '[ariaButtonClick]',
 | 
			
		||||
})
 | 
			
		||||
export class CoreAriaButtonClickDirective implements OnInit {
 | 
			
		||||
export class CoreAriaButtonClickDirective implements OnInit, OnChanges {
 | 
			
		||||
 | 
			
		||||
    protected element: HTMLElement;
 | 
			
		||||
 | 
			
		||||
    @Input() disabled = false;
 | 
			
		||||
    @Output() ariaButtonClick = new EventEmitter();
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
@ -34,10 +35,27 @@ export class CoreAriaButtonClickDirective implements OnInit {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize actions.
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        CoreDom.onActivate(this.element, (event) => this.ariaButtonClick.emit(event));
 | 
			
		||||
        CoreDom.initializeClickableElementA11y(this.element, (event) => this.ariaButtonClick.emit(event));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    ngOnChanges(changes: SimpleChanges): void {
 | 
			
		||||
        if (!changes.disabled) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.element.getAttribute('tabindex') === '0' && this.disabled) {
 | 
			
		||||
            this.element.setAttribute('tabindex', '-1');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.element.getAttribute('tabindex') === '-1' && !this.disabled) {
 | 
			
		||||
            this.element.setAttribute('tabindex', '0');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -610,12 +610,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncCompo
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
 | 
			
		||||
                element.setAttribute('tabindex', '0');
 | 
			
		||||
                element.setAttribute('role', 'button');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            CoreDom.onActivate(element, async (event) => {
 | 
			
		||||
            CoreDom.initializeClickableElementA11y(element, async (event) => {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                event.stopPropagation();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -57,12 +57,7 @@ export class CoreLinkDirective implements OnInit {
 | 
			
		||||
     * Function executed when the component is initialized.
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        if (this.element.tagName != 'BUTTON' && this.element.tagName != 'A') {
 | 
			
		||||
            this.element.setAttribute('tabindex', '0');
 | 
			
		||||
            this.element.setAttribute('role', 'button');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreDom.onActivate(this.element, (event) => this.performAction(event));
 | 
			
		||||
        CoreDom.initializeClickableElementA11y(this.element, (event) => this.performAction(event));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@
 | 
			
		||||
                            class="expandable-status-icon" (ariaButtonClick)="toggleExpand($event, section)"
 | 
			
		||||
                            [attr.aria-label]="(section.expanded ? 'core.collapse' : 'core.expand') | translate"
 | 
			
		||||
                            [attr.aria-expanded]="section.expanded" [attr.aria-controls]="'core-course-index-section-' + section.id"
 | 
			
		||||
                            [class.expandable-status-icon-expanded]="section.expanded" tabindex="0">
 | 
			
		||||
                            [class.expandable-status-icon-expanded]="section.expanded">
 | 
			
		||||
                        </ion-icon>
 | 
			
		||||
                        <ion-icon *ngIf="!section.hasVisibleModules" name="" slot="start" aria-hidden="true" class="expandable-status-icon">
 | 
			
		||||
                        </ion-icon>
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,11 @@
 | 
			
		||||
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0" [class.core-course-module-with-view]="moduleHasView">
 | 
			
		||||
<ion-card *ngIf="module.handlerData && module.visibleoncoursepage !== 0" [class.core-course-module-with-view]="moduleHasView"
 | 
			
		||||
    (click)="moduleClicked($event)" [button]="module.handlerData.action && module.uservisible"
 | 
			
		||||
    [attr.aria-label]="module.handlerData.a11yTitle ? module.handlerData.a11yTitle : null">
 | 
			
		||||
    <ng-container *ngIf="!module.handlerData.loading">
 | 
			
		||||
        <ion-item id="core-course-module-{{module.id}}" detail="false"
 | 
			
		||||
            class="ion-text-wrap core-course-module-handler core-module-main-item {{module.handlerData.class}}"
 | 
			
		||||
            (click)="moduleClicked($event)" [attr.aria-label]="module.handlerData.a11yTitle" [ngClass]="{
 | 
			
		||||
        <ion-item id="core-course-module-{{module.id}}"
 | 
			
		||||
            class="ion-text-wrap core-course-module-handler core-module-main-item {{module.handlerData.class}}" [ngClass]="{
 | 
			
		||||
                'item-dimmed': module.visible === 0 || module.uservisible === false
 | 
			
		||||
            }" [button]="module.handlerData.action && module.uservisible">
 | 
			
		||||
            }">
 | 
			
		||||
 | 
			
		||||
            <core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname"
 | 
			
		||||
                [componentId]="module.instance">
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
<ion-card [class.core-course-list-item]="layout == 'list' || layout == 'listwithenrol'"
 | 
			
		||||
    [class.core-course-list-card]="layout == 'card' || layout == 'summarycard'" [class.item-dimmed]="course.hidden">
 | 
			
		||||
    [class.core-course-list-card]="layout == 'card' || layout == 'summarycard'" [class.item-dimmed]="course.hidden" (click)="openCourse()"
 | 
			
		||||
    button [attr.aria-label]="course.displayname || course.fullname">
 | 
			
		||||
 | 
			
		||||
    <div *ngIf="layout == 'card' || layout == 'summarycard'" (click)="openCourse()" class="core-course-thumb"
 | 
			
		||||
        [class.core-course-color-img]="course.courseImage">
 | 
			
		||||
    <div *ngIf="layout == 'card' || layout == 'summarycard'" class="core-course-thumb" [class.core-course-color-img]="course.courseImage">
 | 
			
		||||
        <img *ngIf="course.courseImage" [src]="course.courseImage" core-external-content alt="" />
 | 
			
		||||
        <ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" class="course-icon" aria-hidden="true">
 | 
			
		||||
        </ion-icon>
 | 
			
		||||
@ -27,7 +27,7 @@
 | 
			
		||||
        </div>
 | 
			
		||||
    </ng-container>
 | 
			
		||||
 | 
			
		||||
    <ion-item class="ion-text-wrap" button detail="false" (click)="openCourse()" [attr.aria-label]="course.displayname || course.fullname">
 | 
			
		||||
    <ion-item class="ion-text-wrap">
 | 
			
		||||
 | 
			
		||||
        <ng-container *ngIf="layout == 'list' || layout == 'listwithenrol'">
 | 
			
		||||
            <ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" slot="start" class="course-icon core-course-thumb"
 | 
			
		||||
 | 
			
		||||
@ -1,38 +1,40 @@
 | 
			
		||||
<ion-item button class="ion-text-wrap" (click)="action('download')" *ngIf="downloadCourseEnabled" detail="false">
 | 
			
		||||
    <ion-icon *ngIf="!prefetch.loading" [name]="prefetch.icon" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
    <ion-spinner *ngIf="prefetch.loading" slot="start" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{ prefetch.statusTranslatable | translate }}</h2>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
<ion-item button class="ion-text-wrap" (click)="action('delete')" detail="false"
 | 
			
		||||
    *ngIf="prefetch.status == 'downloaded' || prefetch.status == 'outdated'">
 | 
			
		||||
    <ion-icon name="fas-trash" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{ 'addon.storagemanager.deletedata' | translate }}</h2>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
<ion-item button class="ion-text-wrap" (click)="action('hide')" *ngIf="!course.hidden" detail="false">
 | 
			
		||||
    <ion-icon name="fas-eye" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{ 'core.courses.hidecourse' | translate }}</h2>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
<ion-item button class="ion-text-wrap" (click)="action('show')" *ngIf="course.hidden" detail="false">
 | 
			
		||||
    <ion-icon name="fas-eye-slash" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{ 'core.courses.show' | translate }}</h2>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
<ion-item button class="ion-text-wrap" (click)="action('favourite')" *ngIf="!course.isfavourite" detail="false">
 | 
			
		||||
    <ion-icon name="fas-star" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{ 'core.courses.addtofavourites' | translate }}</h2>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
<ion-item button class="ion-text-wrap" (click)="action('unfavourite')" *ngIf="course.isfavourite" detail="false">
 | 
			
		||||
    <ion-icon name="far-star" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
    <ion-label>
 | 
			
		||||
        <h2>{{ 'core.courses.removefromfavourites' | translate }}</h2>
 | 
			
		||||
    </ion-label>
 | 
			
		||||
</ion-item>
 | 
			
		||||
<ion-list>
 | 
			
		||||
    <ion-item button class="ion-text-wrap" (click)="action('download')" *ngIf="downloadCourseEnabled" detail="false">
 | 
			
		||||
        <ion-icon *ngIf="!prefetch.loading" [name]="prefetch.icon" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
        <ion-spinner *ngIf="prefetch.loading" slot="start" [attr.aria-label]="'core.loading' | translate"></ion-spinner>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <p class="item-heading">{{ prefetch.statusTranslatable | translate }}</p>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
    <ion-item button class="ion-text-wrap" (click)="action('delete')" detail="false"
 | 
			
		||||
        *ngIf="prefetch.status == 'downloaded' || prefetch.status == 'outdated'">
 | 
			
		||||
        <ion-icon name="fas-trash" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <p class="item-heading">{{ 'addon.storagemanager.deletedata' | translate }}</p>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
    <ion-item button class="ion-text-wrap" (click)="action('hide')" *ngIf="!course.hidden" detail="false">
 | 
			
		||||
        <ion-icon name="fas-eye" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <p class="item-heading">{{ 'core.courses.hidecourse' | translate }}</p>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
    <ion-item button class="ion-text-wrap" (click)="action('show')" *ngIf="course.hidden" detail="false">
 | 
			
		||||
        <ion-icon name="fas-eye-slash" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <p class="item-heading">{{ 'core.courses.show' | translate }}</p>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
    <ion-item button class="ion-text-wrap" (click)="action('favourite')" *ngIf="!course.isfavourite" detail="false">
 | 
			
		||||
        <ion-icon name="fas-star" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <p class="item-heading">{{ 'core.courses.addtofavourites' | translate }}</p>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
    <ion-item button class="ion-text-wrap" (click)="action('unfavourite')" *ngIf="course.isfavourite" detail="false">
 | 
			
		||||
        <ion-icon name="far-star" slot="start" aria-hidden="true"></ion-icon>
 | 
			
		||||
        <ion-label>
 | 
			
		||||
            <p class="item-heading">{{ 'core.courses.removefromfavourites' | translate }}</p>
 | 
			
		||||
        </ion-label>
 | 
			
		||||
    </ion-item>
 | 
			
		||||
</ion-list>
 | 
			
		||||
 | 
			
		||||
@ -15,9 +15,9 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div #toolbar class="core-rte-toolbar" [class.toolbar-hidden]="toolbarHidden">
 | 
			
		||||
    <button *ngIf="toolbarArrows" class="toolbar-arrow" [class.toolbar-arrow-hidden]="toolbarPrevHidden" (click)="toolbarPrev($event)"
 | 
			
		||||
    <button *ngIf="toolbarArrows" class="toolbar-arrow" [attr.disabled]="toolbarPrevHidden ? 'true' : null" (click)="toolbarPrev($event)"
 | 
			
		||||
        (keyup)="toolbarPrev($event)" (mousedown)="downAction($event)" (keydown)="downAction($event)"
 | 
			
		||||
        [attr.aria-label]="'core.previous' | translate">
 | 
			
		||||
        [attr.aria-label]="'core.previous' | translate" [tabindex]="toolbarPrevHidden ? -1 : 0">
 | 
			
		||||
        <ion-icon name="fas-chevron-left" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </button>
 | 
			
		||||
    <ion-slides [options]="slidesOpts" [dir]="direction" (ionSlideDidChange)="updateToolbarArrows()">
 | 
			
		||||
@ -25,69 +25,70 @@
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.strong" [title]="'core.editor.bold' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'bold', 'strong')" (keyup)="buttonAction($event, 'bold', 'strong')"
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-bold" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.em" [title]="'core.editor.italic' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'italic', 'em')" (keyup)="buttonAction($event, 'italic', 'em')"
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-italic" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.u" [title]="'core.editor.underline' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'underline', 'u')" (keyup)="buttonAction($event, 'underline', 'u')"
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-underline" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.strike" [title]="'core.editor.strike' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'strikethrough', 'strike')" (keyup)="buttonAction($event, 'strikethrough', 'strike')"
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-strikethrough" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.p" [title]="'core.editor.p' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'p', 'block')" (keyup)="buttonAction($event, 'p', 'block')" (mousedown)="downAction($event)"
 | 
			
		||||
                (keydown)="downAction($event)">
 | 
			
		||||
                (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-paragraph" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h3" [title]="'core.editor.h3' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'h3', 'block')" (keyup)="buttonAction($event, 'h3', 'block')" (mousedown)="downAction($event)"
 | 
			
		||||
                (keydown)="downAction($event)">
 | 
			
		||||
                (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">3</span>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h4" [title]="'core.editor.h4' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'h4', 'block')" (keyup)="buttonAction($event, 'h4', 'block')" (mousedown)="downAction($event)"
 | 
			
		||||
                (keydown)="downAction($event)">
 | 
			
		||||
                (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">4</span>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.h5" [title]="'core.editor.h5' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'h5', 'block')" (keyup)="buttonAction($event, 'h5', 'block')" (mousedown)="downAction($event)"
 | 
			
		||||
                (keydown)="downAction($event)">
 | 
			
		||||
                (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-heading" aria-hidden="true"></ion-icon><span aria-hidden="true">5</span>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.ul" [title]="'core.editor.unorderedlist' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'insertUnorderedList')" (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
                (click)="buttonAction($event, 'insertUnorderedList')" (mousedown)="downAction($event)" (keydown)="downAction($event)"
 | 
			
		||||
                tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-list-ul" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [disabled]="!rteEnabled" [attr.aria-pressed]="toolbarStyles.ol" [title]="'core.editor.orderedlist' | translate"
 | 
			
		||||
                (click)="buttonAction($event, 'insertOrderedList')" (keyup)="buttonAction($event, 'insertOrderedList')"
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-list-ol" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
@ -105,20 +106,20 @@
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide>
 | 
			
		||||
            <button [attr.aria-pressed]="!rteEnabled" [title]="'core.editor.toggle' | translate" (click)="toggleEditor($event)"
 | 
			
		||||
                (keyup)="toggleEditor($event)" (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
                (keyup)="toggleEditor($event)" (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-code" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
        <ion-slide *ngIf="isPhone">
 | 
			
		||||
            <button [title]="'core.editor.hidetoolbar' | translate" (click)="hideToolbar($event, true)" (keyup)="hideToolbar($event, true)"
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
                (mousedown)="downAction($event)" (keydown)="downAction($event)" tabindex="0">
 | 
			
		||||
                <ion-icon name="fas-times" aria-hidden="true"></ion-icon>
 | 
			
		||||
            </button>
 | 
			
		||||
        </ion-slide>
 | 
			
		||||
    </ion-slides>
 | 
			
		||||
    <button *ngIf="toolbarArrows" class="toolbar-arrow" [class.toolbar-arrow-hidden]="toolbarNextHidden"
 | 
			
		||||
    <button *ngIf="toolbarArrows" class="toolbar-arrow" [attr.disabled]="toolbarNextHidden ? 'true' : null"
 | 
			
		||||
        [attr.aria-label]="'core.next' | translate" (click)="toolbarNext($event)" (keyup)="toolbarNext($event)"
 | 
			
		||||
        (mousedown)="downAction($event)" (keydown)="downAction($event)">
 | 
			
		||||
        (mousedown)="downAction($event)" (keydown)="downAction($event)" [tabindex]="toolbarNextHidden ? -1 : 0">
 | 
			
		||||
        <ion-icon name="fas-chevron-right" aria-hidden="true"></ion-icon>
 | 
			
		||||
    </button>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -149,8 +149,9 @@
 | 
			
		||||
                    background-color: var(--toobar-background);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                &.toolbar-arrow-hidden {
 | 
			
		||||
                    opacity: 0;
 | 
			
		||||
                &[disabled],
 | 
			
		||||
                &:disabled {
 | 
			
		||||
                    opacity: .5;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -391,7 +391,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
			
		||||
 | 
			
		||||
        this.stopBubble(event);
 | 
			
		||||
 | 
			
		||||
        const move = event.key == 'ArrowLeft' ? -1 : +1;
 | 
			
		||||
        const move = event.key === 'ArrowLeft' ? -1 : +1;
 | 
			
		||||
        const cursor = this.getCurrentCursorPosition(this.editorElement);
 | 
			
		||||
 | 
			
		||||
        this.setCurrentCursorPosition(this.editorElement, cursor + move);
 | 
			
		||||
@ -754,7 +754,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
 | 
			
		||||
     * @returns Wether space or enter have been pressed.
 | 
			
		||||
     */
 | 
			
		||||
    protected isValidKeyboardKey(event: KeyboardEvent): boolean {
 | 
			
		||||
        return event.key == ' ' || event.key == 'Enter';
 | 
			
		||||
        return event.key === ' ' || event.key === 'Enter';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -353,7 +353,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
 | 
			
		||||
     * @param e Event.
 | 
			
		||||
     */
 | 
			
		||||
    keyDown(e: KeyboardEvent): void {
 | 
			
		||||
        if (e.key == 'Escape') {
 | 
			
		||||
        if (e.key === 'Escape') {
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            e.stopPropagation();
 | 
			
		||||
        }
 | 
			
		||||
@ -365,7 +365,7 @@ export class CoreLoginReconnectPage implements OnInit, OnDestroy {
 | 
			
		||||
     * @param e Event.
 | 
			
		||||
     */
 | 
			
		||||
    keyUp(e: KeyboardEvent): void {
 | 
			
		||||
        if (e.key == 'Escape') {
 | 
			
		||||
        if (e.key === 'Escape') {
 | 
			
		||||
            this.cancel(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
<core-user-avatar *ngIf="(alwaysShow || isMainScreen) && siteInfo" [user]="siteInfo" class="core-bar-button-image clickable"
 | 
			
		||||
    [linkProfile]="false" (ariaButtonClick)="openUserMenu($event)" [userTour]="userTour" role="button" tabindex="0"
 | 
			
		||||
    [linkProfile]="false" (ariaButtonClick)="openUserMenu($event)" [userTour]="userTour"
 | 
			
		||||
    [attr.aria-label]="'core.user.useraccount' | translate">
 | 
			
		||||
</core-user-avatar>
 | 
			
		||||
 | 
			
		||||
@ -514,22 +514,57 @@ export class CoreDom {
 | 
			
		||||
     *
 | 
			
		||||
     * @param element Element to listen to events.
 | 
			
		||||
     * @param callback Callback to call when clicked or the key is pressed.
 | 
			
		||||
     * @deprecated since 4.1.1: Use initializeClickableElementA11y instead.
 | 
			
		||||
     */
 | 
			
		||||
    static onActivate(element: HTMLElement, callback: (event: MouseEvent | KeyboardEvent) => void): void {
 | 
			
		||||
    static onActivate(
 | 
			
		||||
        element: HTMLElement & {disabled?: boolean},
 | 
			
		||||
        callback: (event: MouseEvent | KeyboardEvent) => void,
 | 
			
		||||
    ): void {
 | 
			
		||||
        this.initializeClickableElementA11y(element, callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initializes a clickable element a11y calling the click action when pressed enter or space
 | 
			
		||||
     * and adding tabindex and role if needed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param element Element to listen to events.
 | 
			
		||||
     * @param callback Callback to call when clicked or the key is pressed.
 | 
			
		||||
     */
 | 
			
		||||
    static initializeClickableElementA11y(
 | 
			
		||||
        element: HTMLElement & {disabled?: boolean},
 | 
			
		||||
        callback: (event: MouseEvent | KeyboardEvent) => void,
 | 
			
		||||
    ): void {
 | 
			
		||||
        element.addEventListener('click', (event) => callback(event));
 | 
			
		||||
 | 
			
		||||
        element.addEventListener('keydown', (event) => {
 | 
			
		||||
            if ((event.key == ' ' || event.key == 'Enter')) {
 | 
			
		||||
            if (event.key === ' ' || event.key === 'Enter') {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                event.stopPropagation();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        element.addEventListener('keyup', (event) => {
 | 
			
		||||
            if ((event.key == ' ' || event.key == 'Enter')) {
 | 
			
		||||
            if (event.key === ' ' || event.key === 'Enter') {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                event.stopPropagation();
 | 
			
		||||
 | 
			
		||||
                callback(event);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
 | 
			
		||||
            // Set tabindex if not previously set.
 | 
			
		||||
            if (element.getAttribute('tabindex') === null) {
 | 
			
		||||
                element.setAttribute('tabindex', element.disabled ? '-1' : '0');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Set role if not previously set.
 | 
			
		||||
            if (!element.getAttribute('role')) {
 | 
			
		||||
                element.setAttribute('role', 'button');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            element.classList.add('clickable');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -81,6 +81,7 @@
 | 
			
		||||
 | 
			
		||||
@mixin core-focus-style() {
 | 
			
		||||
    box-shadow: inset 0 0 var(--a11y-focus-width) 1px var(--a11y-focus-color);
 | 
			
		||||
    border-radius: var(--border-radius);
 | 
			
		||||
    // Thicker option:
 | 
			
		||||
    // border: var(--a11y-focus-width) solid var(--a11y-focus-color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -897,12 +897,16 @@ img[core-external-content]:not([src]) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ion-card {
 | 
			
		||||
    box-shadow: var(--box-shadow);
 | 
			
		||||
    margin: var(--ion-card-vertical-margin) var(--ion-card-horizontal-margin);
 | 
			
		||||
    border-width: var(--border-width);
 | 
			
		||||
    border-style: var(--border-style);
 | 
			
		||||
    border-color: var(--border-color);
 | 
			
		||||
    box-shadow: var(--box-shadow);
 | 
			
		||||
    border-radius: var(--border-radius);
 | 
			
		||||
    margin: var(--ion-card-vertical-margin) var(--ion-card-horizontal-margin);
 | 
			
		||||
 | 
			
		||||
    &::part(native) {
 | 
			
		||||
        --border-width: 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ion-item:only-child {
 | 
			
		||||
        --inner-border-width: 0px;
 | 
			
		||||
@ -1514,19 +1518,24 @@ ion-item.item-input.item-multiple-inputs {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Focus highlight for accessibility.
 | 
			
		||||
ion-item.item-input.ion-focused:not(:focus),
 | 
			
		||||
.ion-focused,
 | 
			
		||||
ion-item.ion-activatable.ion-focused:not(:focus),
 | 
			
		||||
.ion-focused:not(.item-multiple-inputs):not(:focus),
 | 
			
		||||
ion-input.has-focus,
 | 
			
		||||
.ion-focused ion-toggle:focus-within,
 | 
			
		||||
.ion-focused ion-select:focus-within,
 | 
			
		||||
.ion-focused ion-checkbox:focus-within,
 | 
			
		||||
.ion-focused ion-radio:focus-within {
 | 
			
		||||
ion-card:focus {
 | 
			
		||||
    @include core-focus();
 | 
			
		||||
}
 | 
			
		||||
.ion-focused.item-multiple-inputs,
 | 
			
		||||
.ion-focused.ion-activatable {
 | 
			
		||||
    ion-toggle:focus-within,
 | 
			
		||||
    ion-select:focus-within,
 | 
			
		||||
    ion-checkbox:focus-within,
 | 
			
		||||
    ion-radio:focus-within {
 | 
			
		||||
        @include core-focus();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Treat cases where there's a focusable element inside an item, like a button.
 | 
			
		||||
ion-item.ion-focused:not(:focus),
 | 
			
		||||
ion-item.item-input:not(.item-multiple-inputs):not(:focus),
 | 
			
		||||
ion-item.item-has-focus:not(.item-multiple-inputs):not(:focus),
 | 
			
		||||
ion-item.item-input ion-input.has-focus {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    &::after {
 | 
			
		||||
@ -1576,13 +1585,17 @@ ion-item.item {
 | 
			
		||||
    outline: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
textarea, button, select, input, a {
 | 
			
		||||
textarea, button, select, input, a, .clickable {
 | 
			
		||||
    &:focus {
 | 
			
		||||
        @include core-focus-style();
 | 
			
		||||
        outline: none;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ion-focused:not(.item-multiple-inputs):not(:focus) .clickable:focus {
 | 
			
		||||
    box-shadow: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ion-loading:focus-visible,
 | 
			
		||||
ion-alert:focus-visible,
 | 
			
		||||
ion-popover:focus-visible,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user