forked from CIT/Vmeda.Online
		
	MOBILE-3653 scorm: Implement index page
This commit is contained in:
		
							parent
							
								
									aebc359083
								
							
						
					
					
						commit
						a2cf8db2ea
					
				
							
								
								
									
										34
									
								
								src/addons/mod/scorm/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/addons/mod/scorm/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					// (C) Copyright 2015 Moodle Pty Ltd.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
 | 
					import { AddonModScormIndexComponent } from './index/index';
 | 
				
			||||||
 | 
					import { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
 | 
					import { CoreCourseComponentsModule } from '@features/course/components/components.module';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					    declarations: [
 | 
				
			||||||
 | 
					        AddonModScormIndexComponent,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    imports: [
 | 
				
			||||||
 | 
					        CoreSharedModule,
 | 
				
			||||||
 | 
					        CoreCourseComponentsModule,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    providers: [
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    exports: [
 | 
				
			||||||
 | 
					        AddonModScormIndexComponent,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModScormComponentsModule {}
 | 
				
			||||||
							
								
								
									
										246
									
								
								src/addons/mod/scorm/components/index/addon-mod-scorm-index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								src/addons/mod/scorm/components/index/addon-mod-scorm-index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,246 @@
 | 
				
			|||||||
 | 
					<!-- Buttons to add to the header. -->
 | 
				
			||||||
 | 
					<core-navbar-buttons slot="end">
 | 
				
			||||||
 | 
					    <core-context-menu>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
 | 
				
			||||||
 | 
					            [href]="externalUrl" iconAction="fas-external-link-alt">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
 | 
				
			||||||
 | 
					            (action)="expandDescription()" iconAction="fas-arrow-right">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}"
 | 
				
			||||||
 | 
					            iconAction="far-newspaper" (action)="gotoBlog()">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="loaded && !hasOffline && isOnline" [priority]="700" [content]="'core.refresh' | translate"
 | 
				
			||||||
 | 
					            (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="loaded && hasOffline && isOnline"  [priority]="600" (action)="doRefresh(null, $event, true)"
 | 
				
			||||||
 | 
					            [content]="'core.settings.synchronizenow' | translate" [iconAction]="syncIcon" [closeOnClick]="false">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="500" [content]="prefetchText" (action)="prefetch($event)"
 | 
				
			||||||
 | 
					            [iconAction]="prefetchStatusIcon" [closeOnClick]="false">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					        <core-context-menu-item *ngIf="size" [priority]="400" [content]="'core.clearstoreddata' | translate:{$a: size}"
 | 
				
			||||||
 | 
					            iconDescription="fas-archive" (action)="removeFiles($event)" iconAction="fas-trash" [closeOnClick]="false">
 | 
				
			||||||
 | 
					        </core-context-menu-item>
 | 
				
			||||||
 | 
					    </core-context-menu>
 | 
				
			||||||
 | 
					</core-navbar-buttons>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- Content. -->
 | 
				
			||||||
 | 
					<core-loading [hideUntil]="loaded" class="core-loading-center">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
 | 
				
			||||||
 | 
					        contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
 | 
				
			||||||
 | 
					    </core-course-module-description>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Warning message. -->
 | 
				
			||||||
 | 
					    <ion-card class="core-info-card" *ngIf="scorm && scorm.warningMessage">
 | 
				
			||||||
 | 
					        <ion-item>
 | 
				
			||||||
 | 
					            <ion-icon name="fas-info-circle" slot="start"></ion-icon>
 | 
				
			||||||
 | 
					            <ion-label>{{ scorm.warningMessage }}</ion-label>
 | 
				
			||||||
 | 
					        </ion-item>
 | 
				
			||||||
 | 
					    </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <ng-container *ngIf="scorm && loaded && !scorm.warningMessage">
 | 
				
			||||||
 | 
					        <!-- Attempts status. -->
 | 
				
			||||||
 | 
					        <ion-card *ngIf="(scorm.displayattemptstatus || offlineAttempts.length) && !skip">
 | 
				
			||||||
 | 
					            <ion-card-header class="ion-text-wrap">
 | 
				
			||||||
 | 
					                <ion-card-title>{{ 'addon.mod_scorm.attempts' | translate }}</ion-card-title>
 | 
				
			||||||
 | 
					            </ion-card-header>
 | 
				
			||||||
 | 
					            <ion-list class="addon-mod_scorm-attempt-summary">
 | 
				
			||||||
 | 
					                <ng-container *ngIf="scorm.displayattemptstatus">
 | 
				
			||||||
 | 
					                    <ion-item class="ion-text-wrap" *ngIf="scorm.maxattempt! >= 0">
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            <h3>{{ 'addon.mod_scorm.noattemptsallowed' | translate }}</h3>
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                        <p slot="end">
 | 
				
			||||||
 | 
					                            <span *ngIf="scorm.maxattempt == 0">{{ 'core.unlimited' | translate }}</span>
 | 
				
			||||||
 | 
					                            <span *ngIf="scorm.maxattempt! > 0">{{ scorm.maxattempt }}</span>
 | 
				
			||||||
 | 
					                        </p>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					                    <ion-item class="ion-text-wrap" *ngIf="numAttempts >= 0">
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            <h3>{{ 'addon.mod_scorm.noattemptsmade' | translate }}</h3>
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                        <p slot="end">{{ numAttempts }}</p>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					                    <ion-item class="ion-text-wrap" *ngFor="let attempt of onlineAttempts">
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            <h3>{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.num}}</h3>
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                        <p slot="end">
 | 
				
			||||||
 | 
					                            <span *ngIf="attempt.grade != -1">{{ attempt.gradeFormatted }}</span>
 | 
				
			||||||
 | 
					                            <span *ngIf="attempt.grade == -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span>
 | 
				
			||||||
 | 
					                        </p>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					                </ng-container>
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-wrap" *ngFor="let attempt of offlineAttempts">
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <h3>{{ 'addon.mod_scorm.gradeforattempt' | translate }} {{attempt.num}}</h3>
 | 
				
			||||||
 | 
					                        <p *ngIf="!scorm.maxattempt || attempt.num <= scorm.maxattempt">
 | 
				
			||||||
 | 
					                            {{ 'addon.mod_scorm.offlineattemptnote' | translate }}
 | 
				
			||||||
 | 
					                        </p>
 | 
				
			||||||
 | 
					                        <p *ngIf="scorm.maxattempt && attempt.num > scorm.maxattempt">
 | 
				
			||||||
 | 
					                            {{ 'addon.mod_scorm.offlineattemptovermax' | translate }}
 | 
				
			||||||
 | 
					                        </p>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                    <p slot="end">
 | 
				
			||||||
 | 
					                        <span *ngIf="attempt.grade != -1">{{ attempt.gradeFormatted }}</span>
 | 
				
			||||||
 | 
					                        <span *ngIf="attempt.grade == -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span>
 | 
				
			||||||
 | 
					                    </p>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-wrap" *ngIf="scorm.displayattemptstatus && gradeMethodReadable">
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <h3>{{ 'addon.mod_scorm.grademethod' | translate }}</h3>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                    <p slot="end">{{ gradeMethodReadable }}</p>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-wrap" *ngIf="scorm.displayattemptstatus && gradeFormatted">
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <h3>{{ 'addon.mod_scorm.gradereported' | translate }}</h3>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                    <p slot="end">
 | 
				
			||||||
 | 
					                        <span *ngIf="grade != -1">{{ gradeFormatted }}</span>
 | 
				
			||||||
 | 
					                        <span *ngIf="grade == -1">{{ 'addon.mod_scorm.cannotcalculategrade' | translate }}</span>
 | 
				
			||||||
 | 
					                    </p>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-wrap" *ngIf="syncTime">
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <h3>{{ 'core.lastsync' | translate }}</h3>
 | 
				
			||||||
 | 
					                        <p>{{ syncTime }}</p>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					            </ion-list>
 | 
				
			||||||
 | 
					        </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Synchronization warning. -->
 | 
				
			||||||
 | 
					        <ion-card class="core-warning-card" *ngIf="!errorMessage && hasOffline">
 | 
				
			||||||
 | 
					            <ion-item>
 | 
				
			||||||
 | 
					                <ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
 | 
				
			||||||
 | 
					                <ion-label>{{ 'core.hasdatatosync' | translate: {$a: moduleName} }}</ion-label>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					        </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- TOC. -->
 | 
				
			||||||
 | 
					        <ion-card *ngIf="scorm && organizations && !skip &&
 | 
				
			||||||
 | 
					            ((scorm.displaycoursestructure && organizations.length) || organizations.length > 1)" class="addon-mod_scorm-toc">
 | 
				
			||||||
 | 
					            <ion-card-header class="ion-text-wrap">
 | 
				
			||||||
 | 
					                <ion-card-title>{{ 'addon.mod_scorm.contents' | translate }}</ion-card-title>
 | 
				
			||||||
 | 
					            </ion-card-header>
 | 
				
			||||||
 | 
					            <ion-list>
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-wrap" *ngIf="organizations.length > 1">
 | 
				
			||||||
 | 
					                    <ion-label>{{ 'addon.mod_scorm.organizations' | translate }}</ion-label>
 | 
				
			||||||
 | 
					                    <ion-select [(ngModel)]="currentOrganization.identifier" (ionChange)="loadOrganization()"
 | 
				
			||||||
 | 
					                        interface="action-sheet">
 | 
				
			||||||
 | 
					                        <ion-select-option *ngFor="let org of organizations" [value]="org.identifier">
 | 
				
			||||||
 | 
					                            {{ org.title }}
 | 
				
			||||||
 | 
					                        </ion-select-option>
 | 
				
			||||||
 | 
					                    </ion-select>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-center" *ngIf="scorm.displaycoursestructure && loadingToc">
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <ion-spinner></ion-spinner>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-wrap" *ngIf="scorm.displaycoursestructure && !loadingToc">
 | 
				
			||||||
 | 
					                    <!-- If data shown doesn't belong to last attempt, show a warning. -->
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <p *ngIf="attemptToContinue">
 | 
				
			||||||
 | 
					                            {{ 'addon.mod_scorm.dataattemptshown' | translate:{number: attemptToContinue} }}
 | 
				
			||||||
 | 
					                        </p>
 | 
				
			||||||
 | 
					                        <p>{{ currentOrganization.title }}</p>
 | 
				
			||||||
 | 
					                        <div *ngFor="let sco of toc" class="core-padding-{{sco.level}} addon-mod_scorm-type-{{sco.scormtype}}">
 | 
				
			||||||
 | 
					                            <p *ngIf="sco.isvisible">
 | 
				
			||||||
 | 
					                                <ion-icon *ngIf="sco.icon" [name]="sco.icon.icon" [attr.aria-label]="sco.icon.description"
 | 
				
			||||||
 | 
					                                    slot="start">
 | 
				
			||||||
 | 
					                                </ion-icon>
 | 
				
			||||||
 | 
					                                <a *ngIf="sco.prereq && sco.launch" (click)="open($event, false, sco.id)" tappable="true">
 | 
				
			||||||
 | 
					                                    <core-format-text [text]="sco.title" contextLevel="module" [contextInstanceId]="module.id"
 | 
				
			||||||
 | 
					                                        [courseId]="courseId">
 | 
				
			||||||
 | 
					                                    </core-format-text>
 | 
				
			||||||
 | 
					                                </a>
 | 
				
			||||||
 | 
					                                <span *ngIf="!sco.prereq || !sco.launch">
 | 
				
			||||||
 | 
					                                    <core-format-text [text]="sco.title" contextLevel="module" [contextInstanceId]="module.id"
 | 
				
			||||||
 | 
					                                        [courseId]="courseId">
 | 
				
			||||||
 | 
					                                    </core-format-text>
 | 
				
			||||||
 | 
					                                </span>
 | 
				
			||||||
 | 
					                                <span *ngIf="accessInfo && accessInfo.canviewscores && sco.scoreraw">
 | 
				
			||||||
 | 
					                                    ({{ 'addon.mod_scorm.score' | translate }}: {{sco.scoreraw}})
 | 
				
			||||||
 | 
					                                </span>
 | 
				
			||||||
 | 
					                            </p>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					            </ion-list>
 | 
				
			||||||
 | 
					        </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Open in browser button. -->
 | 
				
			||||||
 | 
					        <ion-card *ngIf="errorMessage">
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap">
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <p class="text-danger">{{ errorMessage | translate }}</p>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					            <ion-button class="ion-margin ion-text-wrap" expand="block" [href]="externalUrl" core-link>
 | 
				
			||||||
 | 
					                {{ 'core.openinbrowser' | translate }}
 | 
				
			||||||
 | 
					                <ion-icon name="fas-external-link-alt" slot="end"></ion-icon>
 | 
				
			||||||
 | 
					            </ion-button>
 | 
				
			||||||
 | 
					        </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Warning that user doesn't have any more attempts. -->
 | 
				
			||||||
 | 
					        <ion-card *ngIf="!errorMessage && attemptsLeft == 0">
 | 
				
			||||||
 | 
					            <ion-item class="ion-text-wrap">
 | 
				
			||||||
 | 
					                <ion-label>
 | 
				
			||||||
 | 
					                    <p class="text-danger">{{ 'addon.mod_scorm.exceededmaxattempts' | translate }}</p>
 | 
				
			||||||
 | 
					                </ion-label>
 | 
				
			||||||
 | 
					            </ion-item>
 | 
				
			||||||
 | 
					        </ion-card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Open SCORM in app form -->
 | 
				
			||||||
 | 
					        <ion-card *ngIf="!errorMessage && scorm && (!scorm.lastattemptlock || attemptsLeft > 0)">
 | 
				
			||||||
 | 
					            <ion-list>
 | 
				
			||||||
 | 
					                <ng-container *ngIf="!downloading && !skip">
 | 
				
			||||||
 | 
					                    <!-- Create new attempt -->
 | 
				
			||||||
 | 
					                    <ion-item class="ion-text-wrap"
 | 
				
			||||||
 | 
					                        *ngIf="!scorm.forcenewattempt && numAttempts > 0 && !incomplete && attemptsLeft > 0">
 | 
				
			||||||
 | 
					                        <ion-label>{{ 'addon.mod_scorm.newattempt' | translate }}</ion-label>
 | 
				
			||||||
 | 
					                        <ion-checkbox slot="end" name="newAttempt" [(ngModel)]="startNewAttempt">
 | 
				
			||||||
 | 
					                        </ion-checkbox>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <ion-item class="ion-text-wrap" *ngIf="statusMessage">
 | 
				
			||||||
 | 
					                        <ion-label>
 | 
				
			||||||
 | 
					                            <p>{{ statusMessage | translate }}</p>
 | 
				
			||||||
 | 
					                        </ion-label>
 | 
				
			||||||
 | 
					                    </ion-item>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- Open mode (Preview or Normal) -->
 | 
				
			||||||
 | 
					                    <ion-grid>
 | 
				
			||||||
 | 
					                        <ion-row class="ion-align-items-center">
 | 
				
			||||||
 | 
					                            <ion-col *ngIf="!scorm.hidebrowse">
 | 
				
			||||||
 | 
					                                <ion-button expand="block" fill="outline" (click)="open($event, true)" class="ion-text-wrap">
 | 
				
			||||||
 | 
					                                    {{ 'addon.mod_scorm.browse' | translate }}
 | 
				
			||||||
 | 
					                                    <ion-icon name="fas-search" slot="end"></ion-icon>
 | 
				
			||||||
 | 
					                                </ion-button>
 | 
				
			||||||
 | 
					                            </ion-col>
 | 
				
			||||||
 | 
					                            <ion-col>
 | 
				
			||||||
 | 
					                                <ion-button expand="block" (click)="open($event)" class="ion-text-wrap">
 | 
				
			||||||
 | 
					                                    {{ 'addon.mod_scorm.enter' | translate }}
 | 
				
			||||||
 | 
					                                    <ion-icon name="fas-arrow-right" slot="end"></ion-icon>
 | 
				
			||||||
 | 
					                                </ion-button>
 | 
				
			||||||
 | 
					                            </ion-col>
 | 
				
			||||||
 | 
					                        </ion-row>
 | 
				
			||||||
 | 
					                    </ion-grid>
 | 
				
			||||||
 | 
					                </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- Download progress. -->
 | 
				
			||||||
 | 
					                <ion-item class="ion-text-center" *ngIf="downloading">
 | 
				
			||||||
 | 
					                    <ion-label>
 | 
				
			||||||
 | 
					                        <ion-spinner></ion-spinner>
 | 
				
			||||||
 | 
					                        <h2 *ngIf="progressMessage">{{ progressMessage | translate }}</h2>
 | 
				
			||||||
 | 
					                        <core-progress-bar *ngIf="showPercentage" [progress]="percentage"></core-progress-bar>
 | 
				
			||||||
 | 
					                    </ion-label>
 | 
				
			||||||
 | 
					                </ion-item>
 | 
				
			||||||
 | 
					            </ion-list>
 | 
				
			||||||
 | 
					        </ion-card>
 | 
				
			||||||
 | 
					    </ng-container>
 | 
				
			||||||
 | 
					</core-loading>
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/addons/mod/scorm/components/index/index.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/addons/mod/scorm/components/index/index.scss
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					@import "~theme/globals";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:host {
 | 
				
			||||||
 | 
					    .addon-mod_scorm-attempt-summary ion-item > p {
 | 
				
			||||||
 | 
					        font-size: 14px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .addon-mod_scorm-toc {
 | 
				
			||||||
 | 
					        // Hide all non sco icons using css to maintain padding.
 | 
				
			||||||
 | 
					        ion-icon {
 | 
				
			||||||
 | 
					            opacity: 0;
 | 
				
			||||||
 | 
					            @include margin(5px, 8px, null, null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .addon-mod_scorm-type-sco ion-icon {
 | 
				
			||||||
 | 
					            opacity: 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										605
									
								
								src/addons/mod/scorm/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										605
									
								
								src/addons/mod/scorm/components/index/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,605 @@
 | 
				
			|||||||
 | 
					// (C) Copyright 2015 Moodle Pty Ltd.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { CoreConstants } from '@/core/constants';
 | 
				
			||||||
 | 
					import { Component, OnInit, Optional } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component';
 | 
				
			||||||
 | 
					import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
 | 
				
			||||||
 | 
					import { CoreCourse } from '@features/course/services/course';
 | 
				
			||||||
 | 
					import { IonContent } from '@ionic/angular';
 | 
				
			||||||
 | 
					import { CoreNavigator } from '@services/navigator';
 | 
				
			||||||
 | 
					import { CoreDomUtils } from '@services/utils/dom';
 | 
				
			||||||
 | 
					import { CoreUtils } from '@services/utils/utils';
 | 
				
			||||||
 | 
					import { Translate } from '@singletons';
 | 
				
			||||||
 | 
					import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
				
			||||||
 | 
					import { AddonModScormPrefetchHandler } from '../../services/handlers/prefetch';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModScorm,
 | 
				
			||||||
 | 
					    AddonModScormAttemptCountResult,
 | 
				
			||||||
 | 
					    AddonModScormGetScormAccessInformationWSResponse,
 | 
				
			||||||
 | 
					    AddonModScormAttemptGrade,
 | 
				
			||||||
 | 
					    AddonModScormOrganization,
 | 
				
			||||||
 | 
					    AddonModScormProvider,
 | 
				
			||||||
 | 
					    AddonModScormScorm,
 | 
				
			||||||
 | 
					} from '../../services/scorm';
 | 
				
			||||||
 | 
					import { AddonModScormHelper, AddonModScormTOCScoWithIcon } from '../../services/scorm-helper';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    AddonModScormAutoSyncEventData,
 | 
				
			||||||
 | 
					    AddonModScormSync,
 | 
				
			||||||
 | 
					    AddonModScormSyncProvider,
 | 
				
			||||||
 | 
					    AddonModScormSyncResult,
 | 
				
			||||||
 | 
					} from '../../services/scorm-sync';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Component that displays a SCORM entry page.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'addon-mod-scorm-index',
 | 
				
			||||||
 | 
					    templateUrl: 'addon-mod-scorm-index.html',
 | 
				
			||||||
 | 
					    styleUrls: ['index.scss'],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    component = AddonModScormProvider.COMPONENT;
 | 
				
			||||||
 | 
					    moduleName = 'scorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scorm?: AddonModScormScorm; // The SCORM object.
 | 
				
			||||||
 | 
					    currentOrganization: Partial<AddonModScormOrganization> = {}; // Selected organization.
 | 
				
			||||||
 | 
					    startNewAttempt = false;
 | 
				
			||||||
 | 
					    errorMessage?: string; // Error message.
 | 
				
			||||||
 | 
					    syncTime?: string; // Last sync time.
 | 
				
			||||||
 | 
					    hasOffline = false; // Whether the SCORM has offline data.
 | 
				
			||||||
 | 
					    attemptToContinue?: number; // The attempt to continue or review.
 | 
				
			||||||
 | 
					    statusMessage?: string; // Message about the status.
 | 
				
			||||||
 | 
					    downloading = false; // Whether the SCORM is being downloaded.
 | 
				
			||||||
 | 
					    percentage?: string; // Download/unzip percentage.
 | 
				
			||||||
 | 
					    showPercentage = false; // Whether to show the percentage.
 | 
				
			||||||
 | 
					    progressMessage?: string; // Message about download/unzip.
 | 
				
			||||||
 | 
					    organizations?: AddonModScormOrganization[]; // List of organizations.
 | 
				
			||||||
 | 
					    loadingToc = false; // Whether the TOC is being loaded.
 | 
				
			||||||
 | 
					    toc?: AddonModScormTOCScoWithIcon[]; // Table of contents (structure).
 | 
				
			||||||
 | 
					    accessInfo?: AddonModScormGetScormAccessInformationWSResponse; // Access information.
 | 
				
			||||||
 | 
					    skip?: boolean; // Launch immediately.
 | 
				
			||||||
 | 
					    incomplete = false; // Whether last attempt is incomplete.
 | 
				
			||||||
 | 
					    numAttempts = -1; // Number of attempts.
 | 
				
			||||||
 | 
					    grade?: number; // Grade.
 | 
				
			||||||
 | 
					    gradeFormatted?: string; // Grade formatted.
 | 
				
			||||||
 | 
					    gradeMethodReadable?: string; // Grade method in a readable format.
 | 
				
			||||||
 | 
					    attemptsLeft = -1; // Number of attempts left.
 | 
				
			||||||
 | 
					    onlineAttempts: AttemptGrade[] = []; // Grades for online attempts.
 | 
				
			||||||
 | 
					    offlineAttempts: AttemptGrade[] = []; // Grades for offline attempts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected fetchContentDefaultError = 'addon.mod_scorm.errorgetscorm'; // Default error to show when loading contents.
 | 
				
			||||||
 | 
					    protected syncEventName = AddonModScormSyncProvider.AUTO_SYNCED;
 | 
				
			||||||
 | 
					    protected attempts?: AddonModScormAttemptCountResult; // Data about online and offline attempts.
 | 
				
			||||||
 | 
					    protected lastAttempt?: number; // Last attempt.
 | 
				
			||||||
 | 
					    protected lastIsOffline = false; // Whether the last attempt is offline.
 | 
				
			||||||
 | 
					    protected hasPlayed = false; // Whether the user has opened the player page.
 | 
				
			||||||
 | 
					    protected dataSentObserver?: CoreEventObserver; // To detect data sent to server.
 | 
				
			||||||
 | 
					    protected dataSent = false; // Whether some data was sent to server while playing the SCORM.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(
 | 
				
			||||||
 | 
					        protected content?: IonContent,
 | 
				
			||||||
 | 
					        @Optional() courseContentsPage?: CoreCourseContentsPage,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        super('AddonModScormIndexComponent', content, courseContentsPage);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Component being initialized.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async ngOnInit(): Promise<void> {
 | 
				
			||||||
 | 
					        super.ngOnInit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.loadContent(false, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!this.scorm) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.skip) {
 | 
				
			||||||
 | 
					            this.open();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await AddonModScorm.logView(this.scorm.id, this.scorm.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.checkCompletion();
 | 
				
			||||||
 | 
					        } catch {
 | 
				
			||||||
 | 
					            // Ignore errors.
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check the completion.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected checkCompletion(): void {
 | 
				
			||||||
 | 
					        CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Download a SCORM package or restores an ongoing download.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async downloadScormPackage(): Promise<void> {
 | 
				
			||||||
 | 
					        this.downloading = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await AddonModScormPrefetchHandler.download(this.module, this.courseId, undefined, (data) => {
 | 
				
			||||||
 | 
					                if (!data) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                this.percentage = undefined;
 | 
				
			||||||
 | 
					                this.showPercentage = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (data.downloading) {
 | 
				
			||||||
 | 
					                    // Downloading package.
 | 
				
			||||||
 | 
					                    if (this.scorm!.packagesize && data.progress) {
 | 
				
			||||||
 | 
					                        const percentageNumber = Number(data.progress.loaded / this.scorm!.packagesize) * 100;
 | 
				
			||||||
 | 
					                        this.percentage = percentageNumber.toFixed(1);
 | 
				
			||||||
 | 
					                        this.showPercentage = percentageNumber >= 0 && percentageNumber <= 100;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else if (data.message) {
 | 
				
			||||||
 | 
					                    // Show a message.
 | 
				
			||||||
 | 
					                    this.progressMessage = data.message;
 | 
				
			||||||
 | 
					                } else if (data.progress && data.progress.loaded && data.progress.total) {
 | 
				
			||||||
 | 
					                    // Unzipping package.
 | 
				
			||||||
 | 
					                    const percentageNumber = Number(data.progress.loaded / data.progress.total) * 100;
 | 
				
			||||||
 | 
					                    this.percentage = percentageNumber.toFixed(1);
 | 
				
			||||||
 | 
					                    this.showPercentage = percentageNumber >= 0 && percentageNumber <= 100;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.progressMessage = undefined;
 | 
				
			||||||
 | 
					            this.percentage = undefined;
 | 
				
			||||||
 | 
					            this.downloading = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Get the SCORM instance.
 | 
				
			||||||
 | 
					            this.scorm = await AddonModScorm.getScorm(this.courseId, this.module.id, { moduleUrl: this.module.url });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.dataRetrieved.emit(this.scorm);
 | 
				
			||||||
 | 
					            this.description = this.scorm.intro || this.description;
 | 
				
			||||||
 | 
					            this.errorMessage = AddonModScorm.isScormUnsupported(this.scorm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (this.scorm.warningMessage) {
 | 
				
			||||||
 | 
					                return; // SCORM is closed or not open yet, we can't get more data.
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (sync) {
 | 
				
			||||||
 | 
					                // Try to synchronize the SCORM.
 | 
				
			||||||
 | 
					                await CoreUtils.ignoreErrors(this.syncActivity(showErrors));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const [syncTime, accessInfo] = await Promise.all([
 | 
				
			||||||
 | 
					                AddonModScormSync.getReadableSyncTime(this.scorm.id),
 | 
				
			||||||
 | 
					                AddonModScorm.getAccessInformation(this.scorm.id, { cmId: this.module.id }),
 | 
				
			||||||
 | 
					                this.fetchAttemptData(this.scorm),
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.syncTime = syncTime;
 | 
				
			||||||
 | 
					            this.accessInfo = accessInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check whether to launch the SCORM immediately.
 | 
				
			||||||
 | 
					            if (typeof this.skip == 'undefined') {
 | 
				
			||||||
 | 
					                this.skip = !this.hasOffline && !this.errorMessage &&
 | 
				
			||||||
 | 
					                    (!this.scorm.lastattemptlock || this.attemptsLeft > 0) &&
 | 
				
			||||||
 | 
					                    this.accessInfo.canskipview && !this.accessInfo.canviewreport &&
 | 
				
			||||||
 | 
					                    this.scorm.skipview! >= AddonModScormProvider.SKIPVIEW_FIRST &&
 | 
				
			||||||
 | 
					                    (this.scorm.skipview == AddonModScormProvider.SKIPVIEW_ALWAYS || this.lastAttempt == 0);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.fillContextMenu(refresh);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetch attempt data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param scorm Scorm.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchAttemptData(scorm: AddonModScormScorm): Promise<void> {
 | 
				
			||||||
 | 
					        // Get the number of attempts.
 | 
				
			||||||
 | 
					        this.attempts = await AddonModScorm.getAttemptCount(scorm.id, { cmId: this.module.id });
 | 
				
			||||||
 | 
					        this.hasOffline = !!this.attempts.offline.length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Determine the attempt that will be continued or reviewed.
 | 
				
			||||||
 | 
					        const attempt = await AddonModScormHelper.determineAttemptToContinue(scorm, this.attempts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.lastAttempt = attempt.num;
 | 
				
			||||||
 | 
					        this.lastIsOffline = attempt.offline;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.lastAttempt != this.attempts.lastAttempt.num) {
 | 
				
			||||||
 | 
					            this.attemptToContinue = this.lastAttempt;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.attemptToContinue = undefined;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check if the last attempt is incomplete.
 | 
				
			||||||
 | 
					        this.incomplete = await AddonModScorm.isAttemptIncomplete(scorm.id, this.lastAttempt, {
 | 
				
			||||||
 | 
					            offline: this.lastIsOffline,
 | 
				
			||||||
 | 
					            cmId: this.module.id,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.numAttempts = this.attempts.total;
 | 
				
			||||||
 | 
					        this.gradeMethodReadable = AddonModScorm.getScormGradeMethod(scorm);
 | 
				
			||||||
 | 
					        this.attemptsLeft = AddonModScorm.countAttemptsLeft(scorm, this.attempts.lastAttempt.num);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (scorm.forcenewattempt == AddonModScormProvider.SCORM_FORCEATTEMPT_ALWAYS ||
 | 
				
			||||||
 | 
					                (scorm.forcenewattempt && !this.incomplete)) {
 | 
				
			||||||
 | 
					            this.startNewAttempt = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all([
 | 
				
			||||||
 | 
					            this.getReportedGrades(scorm, this.attempts),
 | 
				
			||||||
 | 
					            this.fetchStructure(scorm),
 | 
				
			||||||
 | 
					            this.loadPackageSize(scorm),
 | 
				
			||||||
 | 
					            this.setStatusListener(),
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load SCORM package size if needed.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async loadPackageSize(scorm: AddonModScormScorm): Promise<void> {
 | 
				
			||||||
 | 
					        if (scorm.packagesize || this.errorMessage) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // SCORM is supported but we don't have package size. Try to calculate it.
 | 
				
			||||||
 | 
					        scorm.packagesize = await AddonModScorm.calculateScormSize(scorm);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fetch the structure of the SCORM (TOC).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async fetchStructure(scorm: AddonModScormScorm): Promise<void> {
 | 
				
			||||||
 | 
					        this.organizations = await AddonModScorm.getOrganizations(scorm.id, { cmId: this.module.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!this.currentOrganization.identifier) {
 | 
				
			||||||
 | 
					            // Load first organization (if any).
 | 
				
			||||||
 | 
					            if (this.organizations.length) {
 | 
				
			||||||
 | 
					                this.currentOrganization.identifier = this.organizations[0].identifier;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                this.currentOrganization.identifier = '';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return this.loadOrganizationToc(scorm, this.currentOrganization.identifier);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the grade of an attempt and add it to the scorm attempts list.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param attempt The attempt number.
 | 
				
			||||||
 | 
					     * @param offline Whether it's an offline attempt.
 | 
				
			||||||
 | 
					     * @param attempts Object where to add the attempt.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getAttemptGrade(
 | 
				
			||||||
 | 
					        attempt: number,
 | 
				
			||||||
 | 
					        offline: boolean,
 | 
				
			||||||
 | 
					        attempts: Record<number, AddonModScormAttemptGrade>,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					        const grade = await AddonModScorm.getAttemptGrade(this.scorm!, attempt, offline);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        attempts[attempt] = {
 | 
				
			||||||
 | 
					            num: attempt,
 | 
				
			||||||
 | 
					            grade: grade,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the grades of each attempt and the grade of the SCORM.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async getReportedGrades(scorm: AddonModScormScorm, attempts: AddonModScormAttemptCountResult): Promise<void> {
 | 
				
			||||||
 | 
					        const promises: Promise<void>[] = [];
 | 
				
			||||||
 | 
					        const onlineAttempts: Record<number, AttemptGrade> = {};
 | 
				
			||||||
 | 
					        const offlineAttempts: Record<number, AttemptGrade> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Calculate the grade for each attempt.
 | 
				
			||||||
 | 
					        attempts.online.forEach((attempt) => {
 | 
				
			||||||
 | 
					            // Check that attempt isn't in offline to prevent showing the same attempt twice. Offline should be more recent.
 | 
				
			||||||
 | 
					            if (attempts.offline.indexOf(attempt) == -1) {
 | 
				
			||||||
 | 
					                promises.push(this.getAttemptGrade(attempt, false, onlineAttempts));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        attempts.offline.forEach((attempt) => {
 | 
				
			||||||
 | 
					            promises.push(this.getAttemptGrade(attempt, true, offlineAttempts));
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Calculate the grade of the whole SCORM. We only use online attempts to calculate this data.
 | 
				
			||||||
 | 
					        this.grade = AddonModScorm.calculateScormGrade(scorm, onlineAttempts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add the attempts to the SCORM in array format in ASC order, and format the grades.
 | 
				
			||||||
 | 
					        this.onlineAttempts = CoreUtils.objectToArray(onlineAttempts);
 | 
				
			||||||
 | 
					        this.offlineAttempts = CoreUtils.objectToArray(offlineAttempts);
 | 
				
			||||||
 | 
					        this.onlineAttempts.sort((a, b) => a.num - b.num);
 | 
				
			||||||
 | 
					        this.offlineAttempts.sort((a, b) => a.num - b.num);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Now format the grades.
 | 
				
			||||||
 | 
					        this.onlineAttempts.forEach((attempt) => {
 | 
				
			||||||
 | 
					            attempt.gradeFormatted = AddonModScorm.formatGrade(scorm, attempt.grade);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.offlineAttempts.forEach((attempt) => {
 | 
				
			||||||
 | 
					            attempt.gradeFormatted = AddonModScorm.formatGrade(scorm, attempt.grade);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.gradeFormatted = AddonModScorm.formatGrade(scorm, this.grade);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks if sync has succeed from result sync data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param result Data returned on the sync function.
 | 
				
			||||||
 | 
					     * @return If suceed or not.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected hasSyncSucceed(result: AddonModScormSyncResult): boolean {
 | 
				
			||||||
 | 
					        if (result.updated || this.dataSent) {
 | 
				
			||||||
 | 
					            // Check completion status if something was sent.
 | 
				
			||||||
 | 
					            this.checkCompletion();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.dataSent = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * User entered the page that contains the component.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ionViewDidEnter(): void {
 | 
				
			||||||
 | 
					        super.ionViewDidEnter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!this.hasPlayed) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.hasPlayed = false;
 | 
				
			||||||
 | 
					        this.startNewAttempt = false; // Uncheck new attempt.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add a delay to make sure the player has started the last writing calls so we can detect conflicts.
 | 
				
			||||||
 | 
					        setTimeout(() => {
 | 
				
			||||||
 | 
					            this.dataSentObserver?.off(); // Stop listening for changes.
 | 
				
			||||||
 | 
					            this.dataSentObserver = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Refresh data.
 | 
				
			||||||
 | 
					            this.showLoadingAndRefresh(true, false);
 | 
				
			||||||
 | 
					        }, 500);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Perform the invalidate content function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async invalidateContent(): Promise<void> {
 | 
				
			||||||
 | 
					        const promises: Promise<void>[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promises.push(AddonModScorm.invalidateScormData(this.courseId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.scorm) {
 | 
				
			||||||
 | 
					            promises.push(AddonModScorm.invalidateAllScormData(this.scorm.id));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await Promise.all(promises);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Compares sync event data with current data to check if refresh content is needed.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param syncEventData Data receiven on sync observer.
 | 
				
			||||||
 | 
					     * @return True if refresh is needed, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected isRefreshSyncNeeded(syncEventData: AddonModScormAutoSyncEventData): boolean {
 | 
				
			||||||
 | 
					        if (syncEventData.updated && this.scorm && syncEventData.scormId == this.scorm.id) {
 | 
				
			||||||
 | 
					            // Check completion status.
 | 
				
			||||||
 | 
					            this.checkCompletion();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load a organization's TOC.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async loadOrganization(): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await this.loadOrganizationToc(this.scorm!, this.currentOrganization.identifier!);
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            CoreDomUtils.showErrorModalDefault(error, this.fetchContentDefaultError, true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Load the TOC of a certain organization.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param organizationId The organization id.
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async loadOrganizationToc(scorm: AddonModScormScorm, organizationId: string): Promise<void> {
 | 
				
			||||||
 | 
					        if (!scorm.displaycoursestructure) {
 | 
				
			||||||
 | 
					            // TOC is not displayed, no need to load it.
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.loadingToc = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            this.toc = await AddonModScormHelper.getToc(scorm.id, this.lastAttempt!, this.incomplete, {
 | 
				
			||||||
 | 
					                organization: organizationId,
 | 
				
			||||||
 | 
					                offline: this.lastIsOffline,
 | 
				
			||||||
 | 
					                cmId: this.module.id,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Search organization title.
 | 
				
			||||||
 | 
					            this.organizations!.forEach((org) => {
 | 
				
			||||||
 | 
					                if (org.identifier == organizationId) {
 | 
				
			||||||
 | 
					                    this.currentOrganization.title = org.title;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } finally {
 | 
				
			||||||
 | 
					            this.loadingToc = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Open a SCORM. It will download the SCORM package if it's not downloaded or it has changed.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param event Event.
 | 
				
			||||||
 | 
					     * @param scoId SCO that needs to be loaded when the SCORM is opened. If not defined, load first SCO.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async open(event?: Event, preview: boolean = false, scoId?: number): Promise<void> {
 | 
				
			||||||
 | 
					        if (event) {
 | 
				
			||||||
 | 
					            event.preventDefault();
 | 
				
			||||||
 | 
					            event.stopPropagation();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.downloading) {
 | 
				
			||||||
 | 
					            // Scope is being downloaded, abort.
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const isOutdated = this.currentStatus == CoreConstants.OUTDATED;
 | 
				
			||||||
 | 
					        const scorm = this.scorm!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!isOutdated && this.currentStatus != CoreConstants.NOT_DOWNLOADED) {
 | 
				
			||||||
 | 
					            // Already downloaded, open it.
 | 
				
			||||||
 | 
					            this.openScorm(scoId, preview);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // SCORM needs to be downloaded.
 | 
				
			||||||
 | 
					        await AddonModScormHelper.confirmDownload(scorm, isOutdated);
 | 
				
			||||||
 | 
					        // Invalidate WS data if SCORM is outdated.
 | 
				
			||||||
 | 
					        if (isOutdated) {
 | 
				
			||||||
 | 
					            await CoreUtils.ignoreErrors(AddonModScorm.invalidateAllScormData(scorm.id));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await this.downloadScormPackage();
 | 
				
			||||||
 | 
					            // Success downloading, open SCORM if user hasn't left the view.
 | 
				
			||||||
 | 
					            if (!this.isDestroyed) {
 | 
				
			||||||
 | 
					                this.openScorm(scoId, preview);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            if (!this.isDestroyed) {
 | 
				
			||||||
 | 
					                CoreDomUtils.showErrorModalDefault(
 | 
				
			||||||
 | 
					                    error,
 | 
				
			||||||
 | 
					                    Translate.instant('addon.mod_scorm.errordownloadscorm', { name: scorm.name }),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Open a SCORM package.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param scoId SCO ID.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected openScorm(scoId?: number, preview: boolean = false): void {
 | 
				
			||||||
 | 
					        // Display the full page when returning to the page.
 | 
				
			||||||
 | 
					        this.skip = false;
 | 
				
			||||||
 | 
					        this.hasPlayed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Detect if anything was sent to server.
 | 
				
			||||||
 | 
					        this.dataSentObserver?.off();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.dataSentObserver = CoreEvents.on(AddonModScormProvider.DATA_SENT_EVENT, (data) => {
 | 
				
			||||||
 | 
					            if (data.scormId === this.scorm!.id) {
 | 
				
			||||||
 | 
					                this.dataSent = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, this.siteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CoreNavigator.navigate('player', {
 | 
				
			||||||
 | 
					            params: {
 | 
				
			||||||
 | 
					                mode: preview ? AddonModScormProvider.MODEBROWSE : AddonModScormProvider.MODENORMAL,
 | 
				
			||||||
 | 
					                moduleUrl: this.module.url,
 | 
				
			||||||
 | 
					                newAttempt: !!this.startNewAttempt,
 | 
				
			||||||
 | 
					                organizationId: this.currentOrganization.identifier,
 | 
				
			||||||
 | 
					                scoId: scoId,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @inheritdoc
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async showStatus(status: string): Promise<void> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (status == CoreConstants.OUTDATED && this.scorm) {
 | 
				
			||||||
 | 
					            // Only show the outdated message if the file should be downloaded.
 | 
				
			||||||
 | 
					            const download = await AddonModScorm.shouldDownloadMainFile(this.scorm, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.statusMessage = download ? 'addon.mod_scorm.scormstatusoutdated' : '';
 | 
				
			||||||
 | 
					        } else if (status == CoreConstants.NOT_DOWNLOADED) {
 | 
				
			||||||
 | 
					            this.statusMessage = 'addon.mod_scorm.scormstatusnotdownloaded';
 | 
				
			||||||
 | 
					        } else if (status == CoreConstants.DOWNLOADING) {
 | 
				
			||||||
 | 
					            if (!this.downloading) {
 | 
				
			||||||
 | 
					                // It's being downloaded right now but the view isn't tracking it. "Restore" the download.
 | 
				
			||||||
 | 
					                this.downloadScormPackage();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this.statusMessage = '';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Performs the sync of the activity.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Promise resolved when done.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected async sync(): Promise<AddonModScormSyncResult> {
 | 
				
			||||||
 | 
					        const result = await AddonModScormSync.syncScorm(this.scorm!);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!result.updated && this.dataSent) {
 | 
				
			||||||
 | 
					            // The user sent data to server, but not in the sync process. Check if we need to fetch data.
 | 
				
			||||||
 | 
					            await CoreUtils.ignoreErrors(
 | 
				
			||||||
 | 
					                AddonModScormSync.prefetchAfterUpdate(AddonModScormPrefetchHandler.instance, this.module, this.courseId),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Grade for an online attempt.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export type AttemptGrade = AddonModScormAttemptGrade & {
 | 
				
			||||||
 | 
					    gradeFormatted?: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										22
									
								
								src/addons/mod/scorm/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/addons/mod/scorm/pages/index/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					<ion-header>
 | 
				
			||||||
 | 
					    <ion-toolbar>
 | 
				
			||||||
 | 
					        <ion-buttons slot="start">
 | 
				
			||||||
 | 
					            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					        <ion-title>
 | 
				
			||||||
 | 
					            <core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
 | 
				
			||||||
 | 
					            </core-format-text>
 | 
				
			||||||
 | 
					        </ion-title>
 | 
				
			||||||
 | 
					        <ion-buttons slot="end">
 | 
				
			||||||
 | 
					            <!-- The buttons defined by the component will be added in here. -->
 | 
				
			||||||
 | 
					        </ion-buttons>
 | 
				
			||||||
 | 
					    </ion-toolbar>
 | 
				
			||||||
 | 
					</ion-header>
 | 
				
			||||||
 | 
					<ion-content>
 | 
				
			||||||
 | 
					    <ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
 | 
				
			||||||
 | 
					        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
				
			||||||
 | 
					    </ion-refresher>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <addon-mod-scorm-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)">
 | 
				
			||||||
 | 
					    </addon-mod-scorm-index>
 | 
				
			||||||
 | 
					</ion-content>
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/addons/mod/scorm/pages/index/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/addons/mod/scorm/pages/index/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					// (C) Copyright 2015 Moodle Pty Ltd.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Component, OnInit, ViewChild } from '@angular/core';
 | 
				
			||||||
 | 
					import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
 | 
				
			||||||
 | 
					import { AddonModScormIndexComponent } from '../../components/index/index';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Page that displays the scorm entry page.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Component({
 | 
				
			||||||
 | 
					    selector: 'page-addon-mod-scorm-index',
 | 
				
			||||||
 | 
					    templateUrl: 'index.html',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModScormIndexPage extends CoreCourseModuleMainActivityPage<AddonModScormIndexComponent> implements OnInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ViewChild(AddonModScormIndexComponent) activityComponent?: AddonModScormIndexComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										39
									
								
								src/addons/mod/scorm/scorm-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/addons/mod/scorm/scorm-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					// (C) Copyright 2015 Moodle Pty Ltd.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					// you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					// You may obtain a copy of the License at
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//     http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					// distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					// See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { NgModule } from '@angular/core';
 | 
				
			||||||
 | 
					import { RouterModule, Routes } from '@angular/router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { CoreSharedModule } from '@/core/shared.module';
 | 
				
			||||||
 | 
					import { AddonModScormComponentsModule } from './components/components.module';
 | 
				
			||||||
 | 
					import { AddonModScormIndexPage } from './pages/index/index';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: ':courseId/:cmId',
 | 
				
			||||||
 | 
					        component: AddonModScormIndexPage,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@NgModule({
 | 
				
			||||||
 | 
					    imports: [
 | 
				
			||||||
 | 
					        RouterModule.forChild(routes),
 | 
				
			||||||
 | 
					        CoreSharedModule,
 | 
				
			||||||
 | 
					        AddonModScormComponentsModule,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    declarations: [
 | 
				
			||||||
 | 
					        AddonModScormIndexPage,
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AddonModScormLazyModule {}
 | 
				
			||||||
@ -13,25 +13,44 @@
 | 
				
			|||||||
// limitations under the License.
 | 
					// limitations under the License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
 | 
					import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
 | 
				
			||||||
 | 
					import { Routes } from '@angular/router';
 | 
				
			||||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
 | 
					import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
 | 
				
			||||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
 | 
					import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
 | 
				
			||||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
					import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
				
			||||||
 | 
					import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
				
			||||||
import { CoreCronDelegate } from '@services/cron';
 | 
					import { CoreCronDelegate } from '@services/cron';
 | 
				
			||||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
					import { CORE_SITE_SCHEMAS } from '@services/sites';
 | 
				
			||||||
 | 
					import { AddonModScormComponentsModule } from './components/components.module';
 | 
				
			||||||
import { OFFLINE_SITE_SCHEMA } from './services/database/scorm';
 | 
					import { OFFLINE_SITE_SCHEMA } from './services/database/scorm';
 | 
				
			||||||
import { AddonModScormGradeLinkHandler } from './services/handlers/grade-link';
 | 
					import { AddonModScormGradeLinkHandler } from './services/handlers/grade-link';
 | 
				
			||||||
import { AddonModScormIndexLinkHandler } from './services/handlers/index-link';
 | 
					import { AddonModScormIndexLinkHandler } from './services/handlers/index-link';
 | 
				
			||||||
import { AddonModScormListLinkHandler } from './services/handlers/list-link';
 | 
					import { AddonModScormListLinkHandler } from './services/handlers/list-link';
 | 
				
			||||||
import { AddonModScormModuleHandler } from './services/handlers/module';
 | 
					import { AddonModScormModuleHandler, AddonModScormModuleHandlerService } from './services/handlers/module';
 | 
				
			||||||
import { AddonModScormPrefetchHandler } from './services/handlers/prefetch';
 | 
					import { AddonModScormPrefetchHandler } from './services/handlers/prefetch';
 | 
				
			||||||
import { AddonModScormSyncCronHandler } from './services/handlers/sync-cron';
 | 
					import { AddonModScormSyncCronHandler } from './services/handlers/sync-cron';
 | 
				
			||||||
 | 
					import { AddonModScormProvider } from './services/scorm';
 | 
				
			||||||
 | 
					import { AddonModScormHelperProvider } from './services/scorm-helper';
 | 
				
			||||||
 | 
					import { AddonModScormOfflineProvider } from './services/scorm-offline';
 | 
				
			||||||
 | 
					import { AddonModScormSyncProvider } from './services/scorm-sync';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ADDON_MOD_SCORM_SERVICES: Type<unknown>[] = [
 | 
					export const ADDON_MOD_SCORM_SERVICES: Type<unknown>[] = [
 | 
				
			||||||
 | 
					    AddonModScormProvider,
 | 
				
			||||||
 | 
					    AddonModScormOfflineProvider,
 | 
				
			||||||
 | 
					    AddonModScormHelperProvider,
 | 
				
			||||||
 | 
					    AddonModScormSyncProvider,
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const routes: Routes = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        path: AddonModScormModuleHandlerService.PAGE_NAME,
 | 
				
			||||||
 | 
					        loadChildren: () => import('./scorm-lazy.module').then(m => m.AddonModScormLazyModule),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@NgModule({
 | 
					@NgModule({
 | 
				
			||||||
    imports: [
 | 
					    imports: [
 | 
				
			||||||
 | 
					        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
				
			||||||
 | 
					        AddonModScormComponentsModule,
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    providers: [
 | 
					    providers: [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
				
			|||||||
@ -480,3 +480,10 @@ ion-button.core-button-select {
 | 
				
			|||||||
.core-browser-copy-area {
 | 
					.core-browser-copy-area {
 | 
				
			||||||
    display: none;
 | 
					    display: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Different levels of padding.
 | 
				
			||||||
 | 
					@for $i from 0 through 15 {
 | 
				
			||||||
 | 
					    .core-padding-#{$i} {
 | 
				
			||||||
 | 
					        @include padding(null, null, null, 15px * $i + 16px);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user