Merge pull request #3063 from crazyserver/MOBILE-3814

Mobile 3814
main
Dani Palou 2022-01-20 09:10:52 +01:00 committed by GitHub
commit 686f35d544
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 304 additions and 276 deletions

View File

@ -3,6 +3,9 @@
:host {
.core-horizontal-scroll div.core-horizontal-scroll-item {
@include horizontal_scroll_item(80%, 250px, 300px);
ion-card {
height: auto;
}
}
.core-course-module-handler {

View File

@ -190,7 +190,7 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan
protected insertSpan(text: string, start: number, end: number): string {
return CoreTextUtils.substrReplace(
text,
'<span class="nolink">' + text.substr(start, end - start + 1) + '</span>',
'<span class="nolink">' + text.substring(start, end - start + 1) + '</span>',
start,
end - start + 1,
);
@ -265,7 +265,7 @@ export class AddonFilterMathJaxLoaderHandlerService extends CoreFilterDefaultHan
*/
protected fixUseUrls(node: Element): void {
Array.from(node.querySelectorAll('use')).forEach((useElem) => {
useElem.setAttribute('href', useElem.href.baseVal.substr(useElem.href.baseVal.indexOf('#')));
useElem.setAttribute('href', useElem.href.baseVal.substring(useElem.href.baseVal.indexOf('#')));
});
}

View File

@ -40,9 +40,11 @@
[attr.aria-label]="(favourites.expanded ? 'core.collapse' : 'core.expand') | translate"
[attr.aria-expanded]="favourites.expanded" aria-controls="addon-messages-groupconversations-favourite" role="heading"
detail="false">
<ion-icon *ngIf="!favourites.expanded" name="fas-caret-right" flip-rtl slot="start" aria-hidden="true">
<ion-icon *ngIf="!favourites.expanded" name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true"
class="expandable-status-icon">
</ion-icon>
<ion-icon *ngIf="favourites.expanded" name="fas-caret-down" slot="start" aria-hidden="true"></ion-icon>
<ion-icon *ngIf="favourites.expanded" name="fas-chevron-down" slot="start" aria-hidden="true"
class="expandable-status-icon"></ion-icon>
<ion-label>
<h2>{{ 'core.favourites' | translate }} ({{ favourites.count }})</h2>
</ion-label>
@ -74,8 +76,10 @@
<ion-item button class="divider ion-text-wrap" (click)="toggle(group)" sticky="true"
[attr.aria-label]="(group.expanded ? 'core.collapse' : 'core.expand') | translate" [attr.aria-expanded]="group.expanded"
aria-controls="addon-messages-groupconversations-group" role="heading" detail="false">
<ion-icon *ngIf="!group.expanded" name="fas-caret-right" flip-rtl slot="start" aria-hidden="true"></ion-icon>
<ion-icon *ngIf="group.expanded" name="fas-caret-down" slot="start" aria-hidden="true"></ion-icon>
<ion-icon *ngIf="!group.expanded" name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true"
class="expandable-status-icon"></ion-icon>
<ion-icon *ngIf="group.expanded" name="fas-chevron-down" slot="start" aria-hidden="true" class="expandable-status-icon">
</ion-icon>
<ion-label>
<h2>{{ 'addon.messages.groupconversations' | translate }} ({{ group.count }})</h2>
</ion-label>
@ -107,9 +111,11 @@
[attr.aria-label]="(individual.expanded ? 'core.collapse' : 'core.expand') | translate"
[attr.aria-expanded]="individual.expanded" aria-controls="addon-messages-groupconversations-individual" role="heading"
detail="false">
<ion-icon *ngIf="!individual.expanded" name="fas-caret-right" flip-rtl slot="start" aria-hidden="true">
<ion-icon *ngIf="!individual.expanded" name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true"
class="expandable-status-icon">
</ion-icon>
<ion-icon *ngIf="individual.expanded" name="fas-caret-down" slot="start" aria-hidden="true"></ion-icon>
<ion-icon *ngIf="individual.expanded" name="fas-chevron-down" slot="start" aria-hidden="true"
class="expandable-status-icon"></ion-icon>
<ion-label>
<h2>{{ 'addon.messages.individualconversations' | translate }} ({{ individual.count }})</h2>
</ion-label>

View File

@ -30,8 +30,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline">
<ion-list inset="true" description *ngIf="assign && assign.introattachments && assign.introattachments.length">
<core-file *ngFor="let file of assign.introattachments" [file]="file" [component]="component" [componentId]="componentId">
</core-file>
@ -148,4 +148,5 @@
</addon-mod-assign-submission>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id"></core-course-module-navigation>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -23,8 +23,8 @@
<core-loading [hideUntil]="loaded" class="safe-area-page">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId">
</core-course-module-info>
<ng-container *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
@ -126,5 +126,5 @@
</ng-container>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -24,8 +24,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId">
</core-course-module-info>
<ion-card class="core-warning-card" *ngIf="warning">
@ -56,5 +56,5 @@
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -27,8 +27,8 @@
<core-loading [hideUntil]="loaded" class="safe-area-padding">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId">
</core-course-module-info>
<ion-card *ngIf="chatInfo" class="core-info-card">
@ -48,5 +48,5 @@
</ng-container>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -53,14 +53,14 @@ export class AddonModChatHelperProvider {
formattedMessage.message = formattedMessage.message.trim();
formattedMessage.showDate = this.showDate(message, prevMessage);
formattedMessage.beep = (message.message.substr(0, 5) == 'beep ' && message.message.substr(5).trim()) || undefined;
formattedMessage.beep = (message.message.substring(0, 5) == 'beep ' && message.message.substring(5).trim()) || undefined;
formattedMessage.special = !!formattedMessage.beep || (<AddonModChatSessionMessage> message).issystem ||
(<AddonModChatMessage> message).system;
if (formattedMessage.message.substr(0, 4) == '/me ') {
if (formattedMessage.message.substring(0, 4) == '/me ') {
formattedMessage.special = true;
formattedMessage.message = formattedMessage.message.substr(4).trim();
formattedMessage.message = formattedMessage.message.substring(4).trim();
}
if (!formattedMessage.special && formattedMessage.message.match(patternTo)) {

View File

@ -29,8 +29,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline">
</core-course-module-info>
<!-- Activity availability messages -->
@ -155,7 +155,7 @@
</ion-card>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>
<!-- Template to render a choice option label. -->

View File

@ -39,8 +39,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings">
</core-course-module-info>
<ion-item class="ion-text-wrap" *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
@ -138,7 +138,7 @@
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canAdd">

View File

@ -52,7 +52,7 @@ export class AddonModDataFieldDateHandlerService implements AddonModDataFieldHan
const enabledName = 'f_' + field.id + '_z';
if (inputData[enabledName] && typeof inputData[fieldName] == 'string') {
const date = inputData[fieldName].substr(0, 10).split('-');
const date = inputData[fieldName].substring(0, 10).split('-');
return [
{
@ -87,7 +87,7 @@ export class AddonModDataFieldDateHandlerService implements AddonModDataFieldHan
return [];
}
const date = inputData[fieldName].substr(0, 10).split('-');
const date = inputData[fieldName].substring(0, 10).split('-');
return [
{
@ -117,10 +117,10 @@ export class AddonModDataFieldDateHandlerService implements AddonModDataFieldHan
originalFieldData: AddonModDataEntryField,
): boolean {
const fieldName = 'f_' + field.id;
const input = inputData[fieldName] && inputData[fieldName].substr(0, 10) || '';
const input = inputData[fieldName] && inputData[fieldName].substring(0, 10) || '';
const content = (originalFieldData && originalFieldData?.content &&
CoreTimeUtils.toDatetimeFormat(parseInt(originalFieldData.content, 10) * 1000).substr(0, 10)) || '';
CoreTimeUtils.toDatetimeFormat(parseInt(originalFieldData.content, 10) * 1000).substring(0, 10)) || '';
return input != content;
}

View File

@ -29,8 +29,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline">
</core-course-module-info>
<core-tabs [hideUntil]="tabsReady" [selectedIndex]="firstSelectedTab">
@ -55,7 +55,7 @@
</core-tabs>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>
<ng-template #basicInfo>

View File

@ -26,8 +26,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info *ngIf="!subfolder" [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId">
<core-course-module-info *ngIf="!subfolder" [module]="module" [description]="description" [component]="component"
[componentId]="componentId" [courseId]="courseId">
</core-course-module-info>
<ion-list *ngIf="contents && (contents.files.length + contents.folders.length > 0)">
@ -49,5 +49,5 @@
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -53,10 +53,10 @@ export class AddonModFolderHelperProvider {
let completePath = '';
// Remove first and last slash if needed.
if (path.substr(0, 1) === '/') {
path = path.substr(1);
if (path.substring(0, 1) === '/') {
path = path.substring(1);
}
if (path.substr(path.length - 1) === '/') {
if (path.substring(path.length - 1) === '/') {
path = path.slice(0, -1);
}

View File

@ -38,10 +38,9 @@
<core-loading [hideUntil]="discussions && discussions.loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()"
[description]="forum && forum.type != 'single' && description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings">
<ion-item>
<core-course-module-info [module]="module" [description]="forum && forum.type != 'single' && description" [component]="component"
[componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings">
<ion-item lines="none" class="ion-text-wrap">
<ion-label>
{{descriptionNote}}
</ion-label>
@ -140,7 +139,8 @@
</ng-container>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module"
(completionChanged)="onCompletionChange()">
</core-course-module-navigation>
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="forum && canAddDiscussion">

View File

@ -118,8 +118,10 @@
<ion-item button class="divider ion-text-wrap" (click)="toggleAdvanced()" detail="false" [attr.aria-expanded]="advanced"
[attr.aria-controls]="'addon-forum-reply-edit-form-advanced-' + uniqueId"
[attr.aria-label]="(advanced ? 'core.hideadvanced' : 'core.showadvanced') | translate">
<ion-icon *ngIf="!advanced" name="fas-caret-right" flip-rtl slot="start" aria-hidden="true"></ion-icon>
<ion-icon *ngIf="advanced" name="fas-caret-down" slot="start" aria-hidden="true"></ion-icon>
<ion-icon *ngIf="!advanced" name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true"
class="expandable-status-icon"></ion-icon>
<ion-icon *ngIf="advanced" name="fas-chevron-down" slot="start" aria-hidden="true" class="expandable-status-icon">
</ion-icon>
<ion-label>
<h2>{{ 'addon.mod_forum.advanced' | translate }}</h2>
</ion-label>

View File

@ -34,8 +34,10 @@
<ion-item button class="divider ion-text-wrap" (click)="toggleAdvanced()" detail="false" [attr.aria-expanded]="advanced"
[attr.aria-label]="(advanced ? 'core.hideadvanced' : 'core.showadvanced') | translate" role="heading"
aria-controls="addon-mod-forum-new-discussion-advanced">
<ion-icon *ngIf="!advanced" name="fas-caret-right" flip-rtl slot="start" aria-hidden="true"></ion-icon>
<ion-icon *ngIf="advanced" name="fas-caret-down" slot="start" aria-hidden="true"></ion-icon>
<ion-icon *ngIf="!advanced" name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true"
class="expandable-status-icon"></ion-icon>
<ion-icon *ngIf="advanced" name="fas-chevron-down" slot="start" aria-hidden="true" class="expandable-status-icon">
</ion-icon>
<ion-label>
<h2>{{ 'addon.mod_forum.advanced' | translate }}</h2>
</ion-label>

View File

@ -50,8 +50,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info *ngIf="!isSearch" [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings">
<core-course-module-info *ngIf="!isSearch" [module]="module" [description]="description" [component]="component"
[componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline || hasOfflineRatings">
</core-course-module-info>
<ion-list *ngIf="!isSearch && entries && entries.offlineEntries.length > 0">
@ -96,7 +96,8 @@
</core-infinite-loading>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module"
(completionChanged)="onCompletionChange()">
</core-course-module-navigation>
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canAdd">

View File

@ -292,7 +292,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
// Try to get the first letter without HTML tags.
const noTags = CoreTextUtils.cleanTags(entry.concept);
return (noTags || entry.concept).substr(0, 1).toUpperCase();
return (noTags || entry.concept).substring(0, 1).toUpperCase();
};
this.getDivider = getDivider;

View File

@ -36,8 +36,8 @@
<core-loading [hideUntil]="loaded" class="safe-area-padding">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline">
</core-course-module-info>
<!-- Offline disabled. -->
@ -85,5 +85,5 @@
</core-h5p-iframe>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -29,7 +29,7 @@
<core-loading [hideUntil]="loaded" class="safe-area-padding">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()">
<core-course-module-info [module]="module">
</core-course-module-info>
<ion-card class="core-warning-card" *ngIf="warning">
@ -46,5 +46,5 @@
</div>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -155,7 +155,7 @@ export class AddonModImscpProvider {
}
const filePath = CoreTextUtils.concatenatePaths(item.filepath, item.filename);
const filePathAlt = filePath.charAt(0) === '/' ? filePath.substr(1) : '/' + filePath;
const filePathAlt = filePath.charAt(0) === '/' ? filePath.substring(1) : '/' + filePath;
// Check if it's main file.
return filePath === targetFilePath || filePathAlt === targetFilePath;

View File

@ -33,8 +33,8 @@
<ng-template>
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline">
</core-course-module-info>
<!-- Prevent access messages. Only show the first one. -->
@ -298,5 +298,5 @@
</core-tabs>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -760,8 +760,8 @@ export class AddonModLessonProvider {
let ignoreCase = '';
if (useRegExp) {
if (expectedAnswer.substr(-2) == '/i') {
expectedAnswer = expectedAnswer.substr(0, expectedAnswer.length - 2);
if (expectedAnswer.substring(-2) == '/i') {
expectedAnswer = expectedAnswer.substring(0, expectedAnswer.length - 2);
ignoreCase = 'i';
}
} else {
@ -792,12 +792,12 @@ export class AddonModLessonProvider {
isMatch = true;
}
} else { // We are using regular expressions analysis.
const startCode = expectedAnswer.substr(0, 2);
const startCode = expectedAnswer.substring(0, 2);
switch (startCode){
// 1- Check for absence of required string in studentAnswer (coded by initial '--').
case '--':
expectedAnswer = expectedAnswer.substr(2);
expectedAnswer = expectedAnswer.substring(2);
if (!studentAnswer.match(new RegExp('^' + expectedAnswer + '$', ignoreCase))) {
isMatch = true;
}
@ -805,7 +805,7 @@ export class AddonModLessonProvider {
// 2- Check for code for marking wrong strings (coded by initial '++').
case '++': {
expectedAnswer = expectedAnswer.substr(2);
expectedAnswer = expectedAnswer.substring(2);
// Check for one or several matches.
const matches = studentAnswer.match(new RegExp(expectedAnswer, 'g' + ignoreCase));

View File

@ -20,9 +20,8 @@
<core-loading [hideUntil]="loaded" class="safe-area-padding">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()"
[description]="lti && lti.showdescriptionlaunch && description" [component]="component" [componentId]="componentId"
[courseId]="courseId">
<core-course-module-info [module]="module" [description]="lti && lti.showdescriptionlaunch && description" [component]="component"
[componentId]="componentId" [courseId]="courseId">
</core-course-module-info>
<div class="ion-padding">
@ -33,5 +32,5 @@
</div>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -26,8 +26,8 @@
<core-loading [hideUntil]="loaded" class="safe-area-padding">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="displayDescription && description"
[component]="component" [componentId]="componentId" [courseId]="courseId">
<core-course-module-info [module]="module" [description]="displayDescription && description" [component]="component"
[componentId]="componentId" [courseId]="courseId">
</core-course-module-info>
<ion-card class="core-warning-card" *ngIf="warning">
@ -49,5 +49,5 @@
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -52,7 +52,7 @@ export class AddonModPageHelperProvider {
let key = content.filename;
if (content.filepath !== '/') {
// Add the folders without the leading slash.
key = content.filepath.substr(1) + key;
key = content.filepath.substring(1) + key;
}
paths[CoreTextUtils.decodeURIComponent(key)] = url;
}

View File

@ -29,9 +29,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId"
[hasDataToSync]="buttonText && hasOffline && !showStatusSpinner">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="buttonText && hasOffline && !showStatusSpinner">
</core-course-module-info>
<!-- Access rules description messages. -->
@ -227,5 +226,5 @@
</ion-card>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -54,7 +54,7 @@ export class AddonModQuizOfflineProvider {
if (!questionsWithAnswers[slot]) {
questionsWithAnswers[slot] = {
answers: {},
prefix: name.substr(0, name.indexOf(nameWithoutPrefix)),
prefix: name.substring(0, name.indexOf(nameWithoutPrefix)),
};
}
questionsWithAnswers[slot].answers[nameWithoutPrefix] = answers[name];

View File

@ -21,7 +21,7 @@
<core-loading [hideUntil]="loaded" class="safe-area-padding">
<!-- Activity info. -->
<core-course-module-info [module]="module" [courseId]="courseId" (completionChanged)="onCompletionChange()"
<core-course-module-info [module]="module" [courseId]="courseId"
[description]="mode != 'iframe' && (mode != 'embedded' || displayDescription) && description" [component]="component"
[componentId]="componentId">
</core-course-module-info>
@ -61,5 +61,5 @@
</ng-container>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -70,7 +70,7 @@ export class AddonModResourceHelperProvider {
let mainFilePath = mainFile.filename;
if (mainFile.filepath !== '/') {
mainFilePath = mainFile.filepath.substr(1) + mainFilePath;
mainFilePath = mainFile.filepath.substring(1) + mainFilePath;
}
try {

View File

@ -201,7 +201,7 @@ export class AddonModScormDataModel12 {
for (const element in this.currentUserData[this.scoId].userdata) {
// Ommit for example the nav. elements and the session time element.
if (element.substr(0, 3) != 'cmi' || element == 'cmi.core.session_time') {
if (element.substring(0, 3) != 'cmi' || element == 'cmi.core.session_time') {
continue;
}
@ -748,16 +748,16 @@ export class AddonModScormDataModel12 {
const childrenStr = '._children';
const countStr = '._count';
if (elementModel.substr(elementModel.length - childrenStr.length, elementModel.length) == childrenStr) {
const parentModel = elementModel.substr(0, elementModel.length - childrenStr.length);
if (elementModel.substring(elementModel.length - childrenStr.length, elementModel.length) == childrenStr) {
const parentModel = elementModel.substring(0, elementModel.length - childrenStr.length);
if (this.dataModel[this.scoId][parentModel] !== undefined) {
this.errorCode = '202';
} else {
this.errorCode = '201';
}
} else if (elementModel.substr(elementModel.length - countStr.length, elementModel.length) == countStr) {
const parentModel = elementModel.substr(0, elementModel.length - countStr.length);
} else if (elementModel.substring(elementModel.length - countStr.length, elementModel.length) == countStr) {
const parentModel = elementModel.substring(0, elementModel.length - countStr.length);
if (this.dataModel[this.scoId][parentModel] !== undefined) {
this.errorCode = '203';

View File

@ -29,8 +29,8 @@
<core-loading [hideUntil]="loaded" class="safe-area-padding">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="!errorMessage && hasOffline">
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="!errorMessage && hasOffline">
</core-course-module-info>
<!-- Warning message. -->
@ -237,5 +237,5 @@
</ng-container>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -947,7 +947,7 @@ export class AddonModScormProvider {
if (parameters) {
const connector = launchUrl.indexOf('?') > -1 ? '&' : '?';
if (parameters.charAt(0) == '?') {
parameters = parameters.substr(1);
parameters = parameters.substring(1);
}
launchUrl += connector + parameters;
@ -1315,7 +1315,7 @@ export class AddonModScormProvider {
if (link.match(/^https?:\/\//i) && !CoreUrlUtils.isLocalFileUrl(link)) {
return true;
} else if (link.substr(0, 4) == 'www.') {
} else if (link.substring(0, 4) == 'www.') {
return true;
}

View File

@ -30,9 +30,8 @@
<core-loading [hideUntil]="loaded" class="safe-area-padding">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()"
[description]="survey && !survey.surveydone && !hasOffline && description" [component]="component" [componentId]="componentId"
[courseId]="courseId" [hasDataToSync]="hasOffline">
<core-course-module-info [module]="module" [description]="survey && !survey.surveydone && !hasOffline && description"
[component]="component" [componentId]="componentId" [courseId]="courseId" [hasDataToSync]="hasOffline">
</core-course-module-info>
<!-- Survey already done -->
@ -148,5 +147,5 @@
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -16,8 +16,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="displayDescription && description"
[component]="component" [componentId]="componentId" [courseId]="courseId">
<core-course-module-info [module]="module" [description]="displayDescription && description" [component]="component"
[componentId]="componentId" [courseId]="courseId">
</core-course-module-info>
<div *ngIf="shouldIframe || (shouldEmbed && isOther)" class="addon-mod_url-embedded-url">
@ -53,5 +53,5 @@
</ion-list>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -68,7 +68,7 @@ export class AddonModUrlProvider {
const download = ['application/zip', 'application/x-tar', 'application/g-zip', 'application/pdf', 'text/html'];
let mimetype = CoreMimetypeUtils.getMimeType(extension);
if (url.externalurl.indexOf('.php') != -1 || url.externalurl.substr(-1) === '/' ||
if (url.externalurl.indexOf('.php') != -1 || url.externalurl.substring(-1) === '/' ||
(url.externalurl.indexOf('//') != -1 && url.externalurl.match(/\//g)?.length == 2)) {
// Seems to be a web, use HTML mimetype.
mimetype = 'text/html';
@ -158,7 +158,7 @@ export class AddonModUrlProvider {
const matches = url.match(/\//g);
const extension = CoreMimetypeUtils.getFileExtension(url);
if (!matches || matches.length < 3 || url.substr(-1) === '/' || extension == 'php') {
if (!matches || matches.length < 3 || url.substring(-1) === '/' || extension == 'php') {
// Use default icon.
return '';
}

View File

@ -48,9 +48,8 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [description]="description"
[component]="component" [componentId]="componentId" [courseId]="courseId">
<h2 *ngIf="pageTitle" title>{{pageTitle}}</h2>
<core-course-module-info [module]="module" [description]="description" [component]="component" [componentId]="componentId"
[courseId]="courseId">
</core-course-module-info>
<div *ngIf="pageIsOffline || hasOffline || pageWarning">
@ -74,6 +73,7 @@
</ion-card>
</div>
<div class="ion-padding addon-mod_wiki-page-content">
<h2 *ngIf="pageTitle">{{pageTitle}}</h2>
<article [ngClass]="{'addon-mod_wiki-noedit': !canEdit}">
<core-format-text *ngIf="pageContent" [component]="component" [componentId]="componentId" [text]="pageContent"
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
@ -89,7 +89,7 @@
</div>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>
<ion-fab slot="fixed" core-fab vertical="bottom" horizontal="end" *ngIf="canEdit">

View File

@ -427,7 +427,6 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp
const pageContents = await this.fetchPageContents(this.currentPage);
if (pageContents) {
this.dataRetrieved.emit(pageContents.title);
this.setSelectedWiki(pageContents.subwikiid, pageContents.userid, pageContents.groupid);
this.pageTitle = pageContents.title;

View File

@ -8,10 +8,6 @@
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
</core-format-text>
</h1>
<h2>
<core-format-text [text]="pageTitle" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
</core-format-text>
</h2>
</ion-title>
<ion-buttons slot="end">

View File

@ -49,18 +49,6 @@ export class AddonModWikiIndexPage extends CoreCourseModuleMainActivityPage<Addo
this.groupId = CoreNavigator.getRouteNumberParam('groupId');
}
/**
* @inheritdoc
*/
updateData(data: { name: string } | string): void {
if (typeof data == 'string') {
// We received the title to display.
this.pageTitle = data;
} else {
super.updateData(data);
}
}
/**
* User entered the page.
*/

View File

@ -30,7 +30,7 @@
<core-loading [hideUntil]="loaded">
<!-- Activity info. -->
<core-course-module-info [module]="module" (completionChanged)="onCompletionChange()" [hasDataToSync]="hasOffline">
<core-course-module-info [module]="module" [hasDataToSync]="hasOffline">
</core-course-module-info>
<ion-card *ngIf="phases">
@ -254,5 +254,5 @@
</div>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id">
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module" (completionChanged)="onCompletionChange()">
</core-course-module-navigation>

View File

@ -226,7 +226,7 @@ export class AddonQtypeCalculatedHandlerService implements CoreQuestionHandler {
}
const numberString = match[0];
const unit = unitsLeft ? answer.substr(0, answer.length - match[0].length) : answer.substr(match[0].length);
const unit = unitsLeft ? answer.substring(0, answer.length - match[0].length) : answer.substring(match[0].length);
// No need to calculate the multiplier.
return { answer: Number(numberString), unit };

View File

@ -58,7 +58,7 @@ export class CoreInterceptor implements HttpInterceptor {
}
}
return query.length ? query.substr(0, query.length - 1) : query;
return query.length ? query.substring(0, query.length - 1) : query;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@ -600,7 +600,7 @@ export class SQLiteDB {
sql = equal ? '= ?' : '<> ?';
params = Array.isArray(items) ? items : [items];
} else {
sql = (equal ? '' : 'NOT ') + 'IN (' + ',?'.repeat(items.length).substr(1) + ')';
sql = (equal ? '' : 'NOT ') + 'IN (' + ',?'.repeat(items.length).substring(1) + ')';
params = items;
}
@ -801,7 +801,7 @@ export class SQLiteDB {
const keys = Object.keys(data);
const fields = keys.join(',');
const questionMarks = ',?'.repeat(keys.length).substr(1);
const questionMarks = ',?'.repeat(keys.length).substring(1);
return {
sql: `INSERT OR REPLACE INTO ${table} (${fields}) VALUES (${questionMarks})`,
@ -1139,7 +1139,7 @@ export class SQLiteDB {
if (params.length == 1) {
sql = sql + field + ' = ?';
} else {
const questionMarks = ',?'.repeat(params.length).substr(1);
const questionMarks = ',?'.repeat(params.length).substring(1);
sql = sql + field + ' IN (' + questionMarks + ')';
}
}

View File

@ -1,23 +1,9 @@
<ion-button
fill="clear"
color="dark"
(click)="scroll('backward')"
[hidden]="scrollPosition === 'hidden'"
[disabled]="scrollPosition === 'start'"
[attr.aria-label]="'core.scrollbackward' | translate"
[attr.aria-controls]="targetId"
>
<ion-icon name="fas-caret-left" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-button fill="clear" color="dark" (click)="scroll('backward')" [hidden]="scrollPosition === 'hidden'"
[disabled]="scrollPosition === 'start'" [attr.aria-label]="'core.scrollbackward' | translate" [attr.aria-controls]="targetId">
<ion-icon name="fas-chevron-left" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button
fill="clear"
color="dark"
(click)="scroll('forward')"
[hidden]="scrollPosition === 'hidden'"
[disabled]="scrollPosition === 'end'"
[attr.aria-label]="'core.scrollforward' | translate"
[attr.aria-controls]="targetId"
>
<ion-icon name="fas-caret-right" slot="icon-only" aria-hidden="true"></ion-icon>
<ion-button fill="clear" color="dark" (click)="scroll('forward')" [hidden]="scrollPosition === 'hidden'"
[disabled]="scrollPosition === 'end'" [attr.aria-label]="'core.scrollforward' | translate" [attr.aria-controls]="targetId">
<ion-icon name="fas-chevron-right" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>

View File

@ -52,16 +52,16 @@ export class CoreFaIconDirective implements AfterViewInit, OnChanges {
switch (parts[0]) {
case 'far':
library = 'regular';
iconName = iconName.substr(4);
iconName = iconName.substring(4);
break;
case 'fa':
case 'fas':
library = 'solid';
iconName = iconName.substr(parts[0].length + 1);
iconName = iconName.substring(parts[0].length + 1);
break;
case 'fab':
library = 'brands';
iconName = iconName.substr(4);
iconName = iconName.substring(4);
break;
default:
break;

View File

@ -140,7 +140,7 @@ export class CoreLinkDirective implements OnInit {
if (href.charAt(0) == '#') {
// Look for id or name.
href = href.substr(1);
href = href.substring(1);
CoreDomUtils.scrollToElementBySelector(
this.element.closest('ion-content'),
this.content,
@ -170,7 +170,7 @@ export class CoreLinkDirective implements OnInit {
* @return Promise resolved when done.
*/
protected async openLocalFile(path: string): Promise<void> {
const filename = path.substr(path.lastIndexOf('/') + 1);
const filename = path.substring(path.lastIndexOf('/') + 1);
if (!CoreFileHelper.isOpenableInApp({ filename })) {
try {

View File

@ -95,7 +95,7 @@ export class CoreContentLinksHandlerBase implements CoreContentLinksHandler {
if (this.pattern) {
const position = url.search(this.pattern);
if (position > -1) {
return url.substr(0, position);
return url.substring(0, position);
}
}
}

View File

@ -2,6 +2,7 @@
<core-mod-icon slot="start" [modicon]="modicon" [modname]="module.modname" [componentId]="module.instance">
</core-mod-icon>
<ion-label>
<p class="core-modulename" *ngIf="moduleNameTranslated">{{moduleNameTranslated}}</p>
<h1>
<core-format-text [text]="module.name" contextLevel="module" [component]="component" [componentId]="componentId"
[contextInstanceId]="module.id" [courseId]="courseId">
@ -18,21 +19,12 @@
</ion-label>
</ion-item>
<ng-content select="[description]"></ng-content>
<ion-item class="ion-text-wrap" lines="none" *ngIf="showCompletion && (module.dates?.length ||
(module.completiondata && (module.completiondata.isautomatic || showManualCompletion) && module.uservisible))">
<!-- Activity dates. -->
<ion-item class="ion-text-wrap core-module-dates" lines="none" *ngIf="showCompletion && module.dates?.length">
<ion-label>
<!-- Activity dates. -->
<div *ngIf="module.dates?.length" class="core-module-dates">
<p *ngFor="let date of module.dates">
<strong>{{ date.label }}</strong> {{ date.timestamp * 1000 | coreFormatDate:'strftimedatetime' }}
</p>
</div>
<!-- Module completion. -->
<core-course-module-completion *ngIf="module.completiondata && module.uservisible" [completion]="module.completiondata"
[moduleName]="module.name" [moduleId]="module.id" [showCompletionConditions]="true"
[showManualCompletion]="showManualCompletion" (completionChanged)="completionChanged.emit($event)">
</core-course-module-completion>
<p *ngFor="let date of module.dates">
<strong>{{ date.label }}</strong> {{ date.timestamp * 1000 | coreFormatDate:'strftimedatetime' }}
</p>
</ion-label>
</ion-item>
<ng-content></ng-content>

View File

@ -6,6 +6,14 @@
margin-bottom: 8px;
padding-bottom: 1px; // To allow margins inside.
background-color: var(--contrast-background);
@include padding-horizontal(var(--ion-safe-area-left), var(--ion-safe-area-right));
.core-modulename {
text-transform: uppercase;
}
core-mod-icon {
align-self: flex-start;
}
}

View File

@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleData, CoreCourseModuleCompletionData } from '@features/course/services/course-helper';
import { CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
import { CoreSites } from '@services/sites';
@ -36,7 +36,6 @@ import { CoreSites } from '@services/sites';
export class CoreCourseModuleInfoComponent implements OnInit {
@Input() module!: CoreCourseModuleData; // The module to render.
@Input() showManualCompletion = true; // Whether to show manual completion, true by default.
@Input() courseId!: number; // The courseId the module belongs to.
@Input() component!: string; // Component for format text directive.
@ -47,8 +46,6 @@ export class CoreCourseModuleInfoComponent implements OnInit {
@Input() hasDataToSync = false; // If the activity has any data to be synced.
@Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when completion changes.
modicon = '';
showCompletion = false; // Whether to show completion.
moduleNameTranslated = '';
@ -60,7 +57,6 @@ export class CoreCourseModuleInfoComponent implements OnInit {
this.modicon = await CoreCourseModuleDelegate.getModuleIconSrc(this.module.modname, this.module.modicon, this.module);
this.moduleNameTranslated = CoreCourse.translateModuleName(this.module.modname || '');
this.showCompletion = CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('3.11');
}

View File

@ -6,6 +6,14 @@
<ion-icon name="fas-arrow-left" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</ion-col>
<ion-col *ngIf="showCompletion && (currentModule.completiondata &&
(currentModule.completiondata.isautomatic || showManualCompletion) && currentModule.uservisible)">
<!-- Module completion. -->
<core-course-module-completion [completion]="currentModule.completiondata" [moduleName]="currentModule.name"
[moduleId]="currentModule.id" [showCompletionConditions]="true" [showManualCompletion]="showManualCompletion"
(completionChanged)="completionChanged.emit($event)">
</core-course-module-completion>
</ion-col>
<ion-col size="auto">
<ion-button fill="clear" class="core-course-next-module" *ngIf="nextModule" (click)="goToActivity(true)"
[attr.aria-label]="'core.course.gotonextactivity' | translate">

View File

@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { CoreCourse, CoreCourseProvider, CoreCourseWSSection } from '@features/course/services/course';
import { CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleCompletionData, CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
import { IonContent } from '@ionic/angular';
import { ScrollDetail } from '@ionic/core';
@ -29,7 +29,7 @@ import { CoreMath } from '@singletons/math';
* Component to show a button to go to the next resource/activity.
*
* Example usage:
* <core-course-module-navigation [courseId]="courseId" [currentModuleId]="module.id"></core-course-module-navigation>
* <core-course-module-navigation [courseId]="courseId" [currentModule]="module"></core-course-module-navigation>
*/
@Component({
selector: 'core-course-module-navigation',
@ -39,13 +39,17 @@ import { CoreMath } from '@singletons/math';
export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
@Input() courseId!: number; // Course ID.
@Input() currentModuleId!: number; // Current module ID.
@Input() currentModule!: CoreCourseModuleData; // Current module.
@Input() showManualCompletion = true; // Whether to show manual completion, true by default.
@Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>(); // Notify when completion changes.
nextModule?: CoreCourseModuleData;
previousModule?: CoreCourseModuleData;
nextModuleSection?: CoreCourseWSSection;
previousModuleSection?: CoreCourseWSSection;
loaded = false;
showCompletion = false; // Whether to show completion.
protected element: HTMLElement;
protected initialHeight = 0;
@ -78,6 +82,8 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
this.showCompletion = CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('3.11');
try {
await this.setNextAndPreviousModules(CoreSitesReadingStrategy.PREFER_CACHE);
} finally {
@ -172,6 +178,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
}
const preSets = CoreSites.getReadingStrategyPreSets(readingStrategy);
const currentModuleId = this.currentModule.id;
const sections = await CoreCourse.getSections(this.courseId, false, true, preSets);
@ -184,7 +191,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
return false;
}
currentModuleIndex = section.modules.findIndex((module: CoreCourseModuleData) => module.id == this.currentModuleId);
currentModuleIndex = section.modules.findIndex((module: CoreCourseModuleData) => module.id == currentModuleId);
return currentModuleIndex >= 0;
});

View File

@ -25,8 +25,7 @@
</ion-refresher>
<core-loading [hideUntil]="loaded">
<core-course-module-info [module]="module" [courseId]="courseId" [description]="module.description" [component]="module.modname"
[componentId]="module.id" (completionChanged)="onCompletionChange()" [expandDescription]="true"
[showManualCompletion]="showManualCompletion">
[componentId]="module.id" [expandDescription]="true">
<div class="safe-area-padding-horizontal ion-padding" *ngIf="module.handlerData?.extraBadge">
<ion-badge class="ion-text-wrap ion-text-start" [color]="module.handlerData?.extraBadgeColor">
@ -61,5 +60,6 @@
</core-course-module-info>
</core-loading>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModuleId]="module.id"></core-course-module-navigation>
<core-course-module-navigation [hidden]="!loaded" [courseId]="courseId" [currentModule]="module"
(completionChanged)="onCompletionChange()" [showManualCompletion]="showManualCompletion"></core-course-module-navigation>
</ion-content>

View File

@ -1,3 +1,10 @@
:host ::ng-deep ion-item-divider {
display: none !important;
}
:host ::ng-deep core-loading {
--internal-loading-inline-min-height: calc(100vh - var(--core-header-toolbar-height));
}
:host-context(ion-tabs.placement-bottom) ::ng-deep core-loading {
--internal-loading-inline-min-height: calc(100vh - var(--core-header-toolbar-height) - var(--bottom-tabs-size) - 2px);
}

View File

@ -198,7 +198,7 @@ export class FileTransferObjectMock extends FileTransferObject {
const headerString = headers[i];
const separatorPos = headerString.indexOf(':');
if (separatorPos != -1) {
result[headerString.substr(0, separatorPos)] = headerString.substr(separatorPos + 1).trim();
result[headerString.substring(0, separatorPos)] = headerString.substring(separatorPos + 1).trim();
}
}
}

View File

@ -444,7 +444,7 @@ export class FileMock extends File {
return {
path: fullPath.substring(0, fullPath.lastIndexOf('/')),
name: fullPath.substr(fullPath.lastIndexOf('/') + 1),
name: fullPath.substring(fullPath.lastIndexOf('/') + 1),
};
}

View File

@ -65,7 +65,7 @@ export class ZipMock extends Zip {
destination = destination.replace(/%20/g, ' ');
const sourceDir = source.substring(0, source.lastIndexOf('/'));
const sourceName = source.substr(source.lastIndexOf('/') + 1);
const sourceName = source.substring(source.lastIndexOf('/') + 1);
const zip = new JSZip();
try {
@ -82,7 +82,7 @@ export class ZipMock extends Zip {
// First of all, create the directory where the files will be unzipped.
const destParent = destination.substring(0, destination.lastIndexOf('/'));
const destFolderName = destination.substr(destination.lastIndexOf('/') + 1);
const destFolderName = destination.substring(destination.lastIndexOf('/') + 1);
await this.file.createDir(destParent, destFolderName, true);
@ -95,7 +95,7 @@ export class ZipMock extends Zip {
if (!file.dir) {
// It's a file.
const fileDir = name.substring(0, name.lastIndexOf('/'));
const fileName = name.substr(name.lastIndexOf('/') + 1);
const fileName = name.substring(name.lastIndexOf('/') + 1);
if (fileDir) {
// The file is in a subfolder, create it first.

View File

@ -37,7 +37,7 @@
</td>
<th class="core-grades-table-gradeitem ion-text-start" [attr.colspan]="row.colspan">
<ion-icon *ngIf="row.expandable && showSummary" aria-hidden="true" slot="start"
[name]="row.expanded ? 'fas-caret-down' : 'fas-caret-right'">
[name]="row.expanded ? 'fas-chevron-down' : 'fas-chevron-right'" class="expandable-status-icon">
</ion-icon>
<ion-icon *ngIf="row.icon" name="{{row.icon}}" slot="start" [attr.aria-label]="row.iconAlt">
</ion-icon>

View File

@ -100,6 +100,11 @@
@include margin(null, null, null, 5px);
}
.expandable-status-icon {
font-size: 14px;
@include margin-horizontal(0, 2px);
}
}
.core-grades-table-feedback {

View File

@ -154,7 +154,7 @@ export class CoreGradesHelperProvider {
for (const name in item) {
const index = name.indexOf('formatted');
if (index > 0) {
item[name.substr(0, index)] = item[name];
item[name.substring(0, index)] = item[name];
}
}
@ -399,7 +399,7 @@ export class CoreGradesHelperProvider {
(row) =>
row.itemname &&
row.itemname.id &&
row.itemname.id.substr(0, 3) == 'row' &&
row.itemname.id.substring(0, 3) == 'row' &&
parseInt(row.itemname.id.split('_')[1], 10) == gradeId,
);

View File

@ -169,7 +169,7 @@ export class CoreH5PContentValidator {
// Check if string is within allowed length.
if (semantics.maxLength !== undefined) {
text = text.substr(0, semantics.maxLength);
text = text.substring(0, semantics.maxLength);
}
return text;
@ -357,8 +357,8 @@ export class CoreH5PContentValidator {
}
// Remove temporary files suffix.
if (file.path.substr(-4, 4) === '#tmp') {
file.path = file.path.substr(0, file.path.length - 4);
if (file.path.substring(-4, 4) === '#tmp') {
file.path = file.path.substring(0, file.path.length - 4);
}
// Make sure path and mime does not have any special chars
@ -670,7 +670,7 @@ export class CoreH5PContentValidator {
const tag = tags[0];
if (tag.substr(0, 1) != '<') {
if (tag.substring(0, 1) != '<') {
// We matched a lone ">" character.
return '&gt;';
} else if (tag.length == 1) {
@ -746,7 +746,7 @@ export class CoreH5PContentValidator {
matches = attr.match(/^([-a-zA-Z]+)/);
if (matches && matches.length > 1) {
attrName = matches[1].toLowerCase();
skip = attrName == 'style' || attrName.substr(0, 2) == 'on' || attrName.substr(0, 1) == '-' ||
skip = attrName == 'style' || attrName.substring(0, 2) == 'on' || attrName.substring(0, 1) == '-' ||
attrName.length > 96; // Ignore long attributes to avoid unnecessary processing overhead.
working = mode = 1;
attr = attr.replace(/^[-a-zA-Z]+/, '');
@ -883,7 +883,7 @@ export class CoreH5PContentValidator {
if (colonPos > 0) {
// We found a colon, possibly a protocol. Verify.
const protocol = uri.substr(0, colonPos);
const protocol = uri.substring(0, colonPos);
// If a colon is preceded by a slash, question mark or hash, it cannot possibly be part of the URL scheme.
// This must be a relative URL, which inherits the (safe) protocol of the base document.
if (protocol.match(/[/?#]/)) {
@ -891,7 +891,7 @@ export class CoreH5PContentValidator {
}
// Check if this is a disallowed protocol.
if (!allowedProtocols[protocol.toLowerCase()]) {
uri = uri.substr(colonPos + 1);
uri = uri.substring(colonPos + 1);
}
}
} while (before != uri);

View File

@ -216,7 +216,7 @@ export class CoreH5PCore {
// Prevent too long slug.
if (newInput.length > 91) {
newInput = newInput.substr(0, 92);
newInput = newInput.substring(0, 92);
}
// Prevent empty slug

View File

@ -14,23 +14,21 @@
</ion-header>
<ion-content>
<ion-list>
<ion-item button class="core-user-profile-maininfo" *ngIf="siteInfo" lines="full" (click)="switchAccounts($event)" detail="true"
[attr.aria-label]="'core.mainmenu.switchaccount' | translate">
<core-user-avatar [user]="siteInfo" [userId]="siteInfo.userid" [linkProfile]="false" slot="start"></core-user-avatar>
<ion-item button class="core-usermenu-siteinfo ion-text-wrap" *ngIf="siteInfo" lines="full" detail="false" [href]="siteUrl"
core-link auto-login="yes">
<ion-label>
<h2>{{ siteInfo.fullname }}</h2>
<p class="core-usermenu-siteinfo core-usermenu-sitename">
<p class="core-usermenu-sitename">
<core-format-text [text]="siteName" contextLevel="system" [contextInstanceId]="0" [wsNotFiltered]="true">
</core-format-text>
</p>
<a [href]="siteUrl" core-link auto-login="yes" class="core-usermenu-siteurl">{{ siteUrl }}</a>
</ion-label>
</ion-item>
<ion-item button class="ion-text-wrap core-usermenu-handler" (click)="openUserProfile($event)"
[attr.aria-label]="'core.user.details' | translate" detail="true" lines="none">
<ion-icon name="fas-user" slot="start" aria-hidden="true"></ion-icon>
<ion-item button class="core-usermenu-handler ion-text-wrap" *ngIf="siteInfo" lines="full" (click)="openUserProfile($event)"
detail="true" [attr.aria-label]="'core.user.profile' | translate">
<core-user-avatar [user]="siteInfo" [userId]="siteInfo.userid" [linkProfile]="false" slot="start"></core-user-avatar>
<ion-label>
<p class="item-heading">{{ 'core.user.profile' | translate }}</p>
<h2>{{ siteInfo.fullname }}</h2>
</ion-label>
</ion-item>
@ -66,10 +64,18 @@
</ion-item>
</ion-list>
</ion-content>
<ion-footer class="ion-padding">
<ion-button (click)="logout($event)" expand="block" color="danger" [attr.aria-label]="'core.mainmenu.logout' | translate"
class="ion-text-wrap">
<ion-icon name="fas-sign-out-alt" slot="start" aria-hidden="true"></ion-icon>
{{ 'core.mainmenu.logout' | translate }}
</ion-button>
<ion-footer>
<ion-item button lines="full" (click)="switchAccounts($event)" detail="true" class="ion-text-wrap">
<ion-icon name="fas-exchange-alt" slot="start" aria-hidden="true"></ion-icon>
<ion-label>
<p class="item-heading">{{ 'core.mainmenu.switchaccount' | translate }}</p>
</ion-label>
</ion-item>
<div class="ion-padding">
<ion-button (click)="logout($event)" expand="block" color="danger" [attr.aria-label]="'core.mainmenu.logout' | translate"
class="ion-text-wrap">
<ion-icon name="fas-sign-out-alt" slot="start" aria-hidden="true"></ion-icon>
{{ 'core.mainmenu.logout' | translate }}
</ion-button>
</div>
</ion-footer>

View File

@ -3,16 +3,25 @@
:host {
.core-user-menu-preferences {
--inner-border-width: 0;
--border-width: 1px 0 0 0;
--border-width: 1px 0 0 0;
}
}
.core-usermenu-siteinfo {
--padding-top: 10px;
--padding-bottom: 10px;
}
@if ($core-user-hide-siteinfo) {
.core-usermenu-siteinfo {
display: none;
}
}
.core-usermenu-sitename {
font-size: 16px;
}
@if ($core-user-hide-sitename) {
.core-usermenu-sitename {
display: none;
@ -24,3 +33,7 @@
display: none;
}
}
ion-footer {
background: var(--contrast-background);
}

View File

@ -303,7 +303,7 @@ export class CoreQuestionHelperProvider {
// Remove start and end of the match, we only want the object.
initMatch = initMatch.replace('M.qtype_' + question.type + '.init_question(', '');
initMatch = initMatch.substr(0, initMatch.length - 2);
initMatch = initMatch.substring(0, initMatch.length - 2);
// Try to convert it to an object and add it to the question.
question.initObjects = CoreTextUtils.parseJSON(initMatch, null);

View File

@ -100,7 +100,7 @@ export class CoreSettingsDeviceInfoPage implements OnDestroy {
if (window.location && window.location.href) {
const url = window.location.href;
this.deviceInfo.locationHref = url.indexOf('#') > 0 ? url.substr(0, url.indexOf('#')) : url;
this.deviceInfo.locationHref = url.indexOf('#') > 0 ? url.substring(0, url.indexOf('#')) : url;
}
if (window.screen) {

View File

@ -30,4 +30,5 @@
(onLoadingContent)="contentLoading()">
</core-site-plugins-plugin-content>
<core-course-module-navigation *ngIf="module" [courseId]="courseId" [currentModuleId]="module.id"></core-course-module-navigation>
<core-course-module-navigation *ngIf="module" [courseId]="courseId" [currentModule]="module">
</core-course-module-navigation>

View File

@ -232,8 +232,8 @@ export class CoreFileProvider {
} else {
// The file plugin doesn't allow creating more than 1 level at a time (e.g. tmp/folder).
// We need to create them 1 by 1.
const firstDir = path.substr(0, path.indexOf('/'));
const restOfPath = path.substr(path.indexOf('/') + 1);
const firstDir = path.substring(0, path.indexOf('/'));
const restOfPath = path.substring(path.indexOf('/') + 1);
this.logger.debug('Create dir ' + firstDir + ' in ' + base);
@ -692,7 +692,7 @@ export class CoreFileProvider {
*/
async removeExternalFile(fullPath: string): Promise<void> {
const directory = fullPath.substring(0, fullPath.lastIndexOf('/'));
const filename = fullPath.substr(fullPath.lastIndexOf('/') + 1);
const filename = fullPath.substring(fullPath.lastIndexOf('/') + 1);
await File.removeFile(directory, filename);
}
@ -885,7 +885,7 @@ export class CoreFileProvider {
};
file.directory = path.substring(0, path.lastIndexOf('/'));
file.name = path.substr(path.lastIndexOf('/') + 1);
file.name = path.substring(path.lastIndexOf('/') + 1);
return file;
}
@ -1035,7 +1035,7 @@ export class CoreFileProvider {
*/
removeStartingSlash(path: string): string {
if (path[0] == '/') {
return path.substr(1);
return path.substring(1);
}
return path;
@ -1232,7 +1232,7 @@ export class CoreFileProvider {
const position = window.location.href.indexOf(window.location.pathname);
if (position != -1) {
return window.location.href.substr(0, position);
return window.location.href.substring(0, position);
}
return window.location.href;

View File

@ -777,7 +777,7 @@ export class CoreFilepoolProvider {
// Calculate the path to the file.
path = file.filename || '';
if (file.filepath && file.filepath !== '/') {
path = file.filepath.substr(1) + path;
path = file.filepath.substring(1) + path;
}
path = CoreTextUtils.concatenatePaths(dirPath, path);
}
@ -871,7 +871,7 @@ export class CoreFilepoolProvider {
// Calculate the path to the file.
path = file.filename || '';
if (file.filepath && file.filepath !== '/') {
path = file.filepath.substr(1) + path;
path = file.filepath.substring(1) + path;
}
path = CoreTextUtils.concatenatePaths(dirPath, path);
}
@ -1837,7 +1837,7 @@ export class CoreFilepoolProvider {
return;
}
const relativePath = url.substr(url.indexOf('/pluginfile.php') + 16);
const relativePath = url.substring(url.indexOf('/pluginfile.php') + 16);
const args = relativePath.split('/');
if (args.length < 3) {
@ -2084,7 +2084,7 @@ export class CoreFilepoolProvider {
// It's a pluginfile URL. Search for the 'file' param to extract the name.
const params = CoreUrlUtils.extractUrlParams(fileUrl);
if (params.file) {
filename = params.file.substr(params.file.lastIndexOf('/') + 1);
filename = params.file.substring(params.file.lastIndexOf('/') + 1);
} else {
// 'file' param not found. Extract what's after the last '/' without params.
filename = CoreUrlUtils.getLastFileWithoutParams(fileUrl);
@ -2115,7 +2115,7 @@ export class CoreFilepoolProvider {
// Remove the URL from the array.
hashes.shift();
filename = filename.substr(0, index);
filename = filename.substring(0, index);
}
// Remove the extension from the filename.

View File

@ -256,7 +256,7 @@ export class CoreLangProvider {
// Language code defined by locale has a dash, like en-US or es-ES. Check if it's supported.
if (CoreConstants.CONFIG.languages && CoreConstants.CONFIG.languages[preferredLanguage] === undefined) {
// Code is NOT supported. Fallback to language without dash. E.g. 'en-US' would fallback to 'en'.
preferredLanguage = preferredLanguage.substr(0, preferredLanguage.indexOf('-'));
preferredLanguage = preferredLanguage.substring(0, preferredLanguage.indexOf('-'));
}
}

View File

@ -249,7 +249,7 @@ export class CoreCustomURLSchemesProvider {
// Remove the params to get the site URL.
if (url.indexOf('?') != -1) {
url = url.substr(0, url.indexOf('?'));
url = url.substring(0, url.indexOf('?'));
}
if (!url.match(/https?:\/\//)) {

View File

@ -443,7 +443,7 @@ export class CoreIframeUtilsProvider {
}
} else if (CoreUrlUtils.isLocalFileUrl(url)) {
// It's a local file.
const filename = url.substr(url.lastIndexOf('/') + 1);
const filename = url.substring(url.lastIndexOf('/') + 1);
if (!CoreFileHelper.isOpenableInApp({ filename })) {
try {
@ -522,7 +522,7 @@ export class CoreIframeUtilsProvider {
// Opening links with _parent, _top or _blank can break the app. We'll open it in InAppBrowser.
event && event.preventDefault();
const filename = link.href.substr(link.href.lastIndexOf('/') + 1);
const filename = link.href.substring(link.href.lastIndexOf('/') + 1);
if (!CoreFileHelper.isOpenableInApp({ filename })) {
try {

View File

@ -84,13 +84,13 @@ export class CoreMimetypeUtilsProvider {
// If the extension has parameters, remove them.
let position = extension.indexOf('?');
if (position > -1) {
extension = extension.substr(0, position);
extension = extension.substring(0, position);
}
// If the extension has an anchor, remove it.
position = extension.indexOf('#');
if (position > -1) {
extension = extension.substr(0, position);
extension = extension.substring(0, position);
}
// Remove hash in extension if there's any (added by filepool).
@ -98,7 +98,7 @@ export class CoreMimetypeUtilsProvider {
// Remove dot from the extension if found.
if (extension && extension[0] == '.') {
extension = extension.substr(1);
extension = extension.substring(1);
}
return extension;
@ -311,12 +311,12 @@ export class CoreMimetypeUtilsProvider {
// Remove params if any.
position = candidate.indexOf('?');
if (position > -1) {
candidate = candidate.substr(0, position);
candidate = candidate.substring(0, position);
}
// Remove anchor if any.
position = candidate.indexOf('#');
if (position > -1) {
candidate = candidate.substr(0, position);
candidate = candidate.substring(0, position);
}
if (EXTENSION_REGEX.test(candidate)) {
@ -346,7 +346,7 @@ export class CoreMimetypeUtilsProvider {
let ext;
if (dot > -1) {
ext = filename.substr(dot + 1).toLowerCase();
ext = filename.substring(dot + 1).toLowerCase();
ext = this.cleanExtension(ext);
// Check extension corresponds to a mimetype to know if it's valid.
@ -589,9 +589,9 @@ export class CoreMimetypeUtilsProvider {
if (position > -1) {
// Check extension corresponds to a mimetype to know if it's valid.
extension = path.substr(position + 1).toLowerCase();
extension = path.substring(position + 1).toLowerCase();
if (this.getMimeType(extension) !== undefined) {
return path.substr(0, position); // Remove extension.
return path.substring(0, position); // Remove extension.
}
}

View File

@ -279,7 +279,7 @@ export class CoreTextUtilsProvider {
const firstCharRight = rightPath.charAt(0);
if (lastCharLeft === '/' && firstCharRight === '/') {
return leftPath + rightPath.substr(1);
return leftPath + rightPath.substring(1);
} else if (lastCharLeft !== '/' && firstCharRight !== '/') {
return leftPath + '/' + rightPath;
} else {
@ -539,7 +539,7 @@ export class CoreTextUtilsProvider {
const url = CoreFileHelper.getFileUrl(files[0]);
// Remove text after last slash (encoded or not).
return url?.substr(0, Math.max(url.lastIndexOf('/'), url.lastIndexOf('%2F')));
return url?.substring(0, Math.max(url.lastIndexOf('/'), url.lastIndexOf('%2F')));
}
return undefined;
@ -778,9 +778,9 @@ export class CoreTextUtilsProvider {
}
// Get the filename from the URL.
let filename = url.substr(url.lastIndexOf('/') + 1);
let filename = url.substring(url.lastIndexOf('/') + 1);
if (filename.indexOf('?') != -1) {
filename = filename.substr(0, filename.indexOf('?'));
filename = filename.substring(0, filename.indexOf('?'));
}
if (pluginfileMap[filename]) {
@ -904,12 +904,12 @@ export class CoreTextUtilsProvider {
*/
shortenText(text: string, length: number): string {
if (text.length > length) {
text = text.substr(0, length);
text = text.substring(0, length);
// Now, truncate at the last word boundary (if exists).
const lastWordPos = text.lastIndexOf(' ');
if (lastWordPos > 0) {
text = text.substr(0, lastWordPos);
text = text.substring(0, lastWordPos);
}
text += '&hellip;';
}

View File

@ -329,9 +329,9 @@ export class CoreUrlUtilsProvider {
* @return Last file without params.
*/
getLastFileWithoutParams(url: string): string {
let filename = url.substr(url.lastIndexOf('/') + 1);
let filename = url.substring(url.lastIndexOf('/') + 1);
if (filename.indexOf('?') != -1) {
filename = filename.substr(0, filename.indexOf('?'));
filename = filename.substring(0, filename.indexOf('?'));
}
return filename;

View File

@ -1254,7 +1254,7 @@ export class CoreUtilsProvider {
const mapped = {};
objects.forEach((item) => {
const keyValue = item[keyName] as string;
const key = prefixSubstr > 0 ? keyValue.substr(prefixSubstr) : keyValue;
const key = prefixSubstr > 0 ? keyValue.substring(prefixSubstr) : keyValue;
mapped[key] = item[valueName];
});

View File

@ -112,7 +112,7 @@ export class CoreColors {
*/
static hexToRGB(color: string): ColorComponents {
if (color.charAt(0) == '#') {
color = color.substr(1);
color = color.substring(1);
}
if (color.length === 3) {
@ -122,9 +122,9 @@ export class CoreColors {
}
return {
red: parseInt(color.substr(0, 2), 16),
green: parseInt(color.substr(2, 2), 16),
blue: parseInt(color.substr(4, 2), 16),
red: parseInt(color.substring(0, 2), 16),
green: parseInt(color.substring(2, 2), 16),
blue: parseInt(color.substring(4, 2), 16),
};
}

View File

@ -123,7 +123,7 @@ function readBytes (str, len, escapedString = false) {
const isLowSurrogate = code >= 0xdc00 && code <= 0xdfff
if (escapedString && chr === '\\') {
chr = String.fromCharCode(parseInt(str.substr(c + 1, 2), 16))
chr = String.fromCharCode(parseInt(str.substring(c + 1, 2), 16))
escapedChars++
// each escaped sequence is 3 characters. Go 2 chars ahead.
@ -167,7 +167,7 @@ function expectString (str) {
const len = parseInt(byteLenMatch, 10)
str = str.substr(match.length)
str = str.substring(match.length)
let [ strMatch, bytes ] = readBytes(str, len)
@ -175,7 +175,7 @@ function expectString (str) {
throw SyntaxError(`Expected string of ${len} bytes, but got ${bytes}`)
}
str = str.substr((strMatch as string).length)
str = str.substring((strMatch as string).length)
// strict parsing, match closing "; chars
if (!str.startsWith('";')) {
@ -195,7 +195,7 @@ function expectEscapedString (str) {
const len = parseInt(strLenMatch, 10)
str = str.substr(match.length)
str = str.substring(match.length)
let [ strMatch, bytes, escapedChars ] = readBytes(str, len, true)
@ -203,7 +203,7 @@ function expectEscapedString (str) {
throw SyntaxError(`Expected escaped string of ${len} bytes, but got ${bytes}`)
}
str = str.substr((strMatch as string).length + (escapedChars as number) * 2)
str = str.substring((strMatch as string).length + (escapedChars as number) * 2)
// strict parsing, match closing "; chars
if (!str.startsWith('";')) {
@ -249,15 +249,15 @@ function expectObject (str, cache) {
const obj = {}
cache([obj])
str = str.substr(totalOffset)
str = str.substring(totalOffset)
for (let i = 0; i < propCount; i++) {
const prop = expectKeyOrIndex(str)
str = str.substr(prop[1])
str = str.substring(prop[1])
totalOffset += prop[1] as number
const value = expectType(str, cache)
str = str.substr(value[1])
str = str.substring(value[1])
totalOffset += value[1]
obj[prop[0]] = value[0]
@ -299,7 +299,7 @@ function expectArray (str, cache) {
throw SyntaxError('Expected array length annotation')
}
str = str.substr(arrayLiteralBeginMatch.length)
str = str.substring(arrayLiteralBeginMatch.length)
const array = expectArrayItems(str, parseInt(arrayLengthMatch, 10), cache)
@ -327,14 +327,14 @@ function expectArrayItems (str, expectedItems = 0, cache) {
hasStringKeys = (typeof key[0] === 'string')
}
str = str.substr(key[1])
str = str.substring(key[1])
totalOffset += key[1]
// references are resolved immediately, so if duplicate key overwrites previous array index
// the old value is anyway resolved
// fixme: but next time the same reference should point to the new value
item = expectType(str, cache)
str = str.substr(item[1])
str = str.substring(item[1])
totalOffset += item[1]
items[key[0]] = item[0]
@ -428,7 +428,7 @@ function substr_replace (str, replace, start, length) {
return [
str.slice(0, start),
replace.substr(0, length),
replace.substring(0, length),
replace.slice(length),
str.slice(start + length)
].join('')

View File

@ -34,7 +34,7 @@ export class CoreText {
}
if (text.slice(-1) == '/') {
return text.substr(0, text.length - 1);
return text.substring(0, text.length - 1);
}
return text;

View File

@ -216,7 +216,7 @@ export class CoreUrl {
return;
}
return url.substr(firstAnchorIndex);
return url.substring(firstAnchorIndex);
}
/**

View File

@ -82,7 +82,7 @@ export class CoreWindow {
*/
static async open(url: string, name?: string): Promise<void> {
if (CoreUrlUtils.isLocalFileUrl(url)) {
const filename = url.substr(url.lastIndexOf('/') + 1);
const filename = url.substring(url.lastIndexOf('/') + 1);
if (!CoreFileHelper.isOpenableInApp({ filename })) {
try {

View File

@ -209,7 +209,7 @@
--vertical-margin: 10px;
--horizontal-margin: 10px;
width: calc(100% - var(--vertical-margin) - var(--vertical-margin));
width: calc(100% - var(--horizontal-margin) - var(--horizontal-margin));
height: calc(100% - var(--vertical-margin) - var(--vertical-margin));
margin: var(--vertical-margin) var(--horizontal-margin);

View File

@ -554,7 +554,7 @@ body.core-iframe-fullscreen ion-router-outlet {
display: block;
height: 100% !important;
width: auto;
min-width: #{$modal-lateral-width};
min-width: calc(#{$modal-lateral-width} - 16px);
box-shadow: 0 28px 48px rgba(0, 0, 0, 0.4);
}
ion-backdrop {

View File

@ -215,6 +215,10 @@
--color: var(--item-divider-color);
--min-height: var(--item-divider-min-height);
min-height: var(--min-height);
.expandable-status-icon {
font-size: 18px;
}
}
--spacer-background: var(--item-divider-background);