MOBILE-3320 lint: Fix linting in utils service
This commit is contained in:
		
							parent
							
								
									e1cf81e9f9
								
							
						
					
					
						commit
						2bba54e38d
					
				@ -202,9 +202,6 @@ export class CoreIframeUtilsProvider {
 | 
			
		||||
            case 'link_clicked':
 | 
			
		||||
                this.linkClicked(event.data.link);
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,7 @@ import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { makeSingleton, Translate, Http } from '@singletons/core.singletons';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
 | 
			
		||||
interface MimeTypeInfo {
 | 
			
		||||
    type: string;
 | 
			
		||||
@ -169,9 +170,11 @@ export class CoreMimetypeUtilsProvider {
 | 
			
		||||
     * @param path Alternative path that will override fileurl from file object.
 | 
			
		||||
     */
 | 
			
		||||
    getEmbeddedHtml(file: CoreWSExternalFile | FileEntry, path?: string): string {
 | 
			
		||||
        const filename = 'isFile' in file ? (file as FileEntry).name : file.filename;
 | 
			
		||||
        const extension = !('isFile' in file) && file.mimetype ? this.getExtension(file.mimetype) : this.getFileExtension(filename);
 | 
			
		||||
        const mimeType = !('isFile' in file) && file.mimetype ? file.mimetype : this.getMimeType(extension);
 | 
			
		||||
        const filename = CoreUtils.instance.isFileEntry(file) ? (file as FileEntry).name : file.filename;
 | 
			
		||||
        const extension = !CoreUtils.instance.isFileEntry(file) && file.mimetype
 | 
			
		||||
            ? this.getExtension(file.mimetype)
 | 
			
		||||
            : this.getFileExtension(filename);
 | 
			
		||||
        const mimeType = !CoreUtils.instance.isFileEntry(file) && file.mimetype ? file.mimetype : this.getMimeType(extension);
 | 
			
		||||
 | 
			
		||||
        // @todo linting: See if this can be removed
 | 
			
		||||
        (file as CoreWSExternalFile).mimetype = mimeType;
 | 
			
		||||
@ -182,7 +185,7 @@ export class CoreMimetypeUtilsProvider {
 | 
			
		||||
            // @todo linting: See if this can be removed
 | 
			
		||||
            (file as { embedType: string }).embedType = embedType;
 | 
			
		||||
 | 
			
		||||
            path = CoreFile.instance.convertFileSrc(path ?? ('isFile' in file ? file.toURL() : file.fileurl));
 | 
			
		||||
            path = CoreFile.instance.convertFileSrc(path ?? (CoreUtils.instance.isFileEntry(file) ? file.toURL() : file.fileurl));
 | 
			
		||||
 | 
			
		||||
            switch (embedType) {
 | 
			
		||||
                case 'image':
 | 
			
		||||
@ -401,7 +404,7 @@ export class CoreMimetypeUtilsProvider {
 | 
			
		||||
        let mimetype = '';
 | 
			
		||||
        let extension = '';
 | 
			
		||||
 | 
			
		||||
        if (typeof obj == 'object' && 'isFile' in obj) {
 | 
			
		||||
        if (typeof obj == 'object' && CoreUtils.instance.isFileEntry(obj)) {
 | 
			
		||||
            // It's a FileEntry. Don't use the file function because it's asynchronous and the type isn't reliable.
 | 
			
		||||
            filename = obj.name;
 | 
			
		||||
        } else if (typeof obj == 'object') {
 | 
			
		||||
 | 
			
		||||
@ -13,42 +13,45 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { Injectable, NgZone } from '@angular/core';
 | 
			
		||||
import { InAppBrowserObject } from '@ionic-native/in-app-browser';
 | 
			
		||||
import { InAppBrowserObject, InAppBrowserOptions } from '@ionic-native/in-app-browser';
 | 
			
		||||
import { FileEntry } from '@ionic-native/file';
 | 
			
		||||
import { Subscription } from 'rxjs';
 | 
			
		||||
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreEvents, CoreEventsProvider } from '@services/events';
 | 
			
		||||
import { CoreFile } from '@services/file';
 | 
			
		||||
import { CoreLang } from '@services/lang';
 | 
			
		||||
import { CoreWS, CoreWSError } from '@services/ws';
 | 
			
		||||
import { CoreWS, CoreWSError, CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import {
 | 
			
		||||
    makeSingleton, Clipboard, InAppBrowser, Platform, FileOpener, WebIntent, QRScanner, Translate
 | 
			
		||||
    makeSingleton, Clipboard, InAppBrowser, Platform, FileOpener, WebIntent, QRScanner, Translate,
 | 
			
		||||
} from '@singletons/core.singletons';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
 | 
			
		||||
type TreeNode<T> = T & { children: TreeNode<T>[] };
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * "Utils" service with helper functions.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class CoreUtilsProvider {
 | 
			
		||||
    protected DONT_CLONE = ['[object FileEntry]', '[object DirectoryEntry]', '[object DOMFileSystem]'];
 | 
			
		||||
 | 
			
		||||
    protected readonly DONT_CLONE = ['[object FileEntry]', '[object DirectoryEntry]', '[object DOMFileSystem]'];
 | 
			
		||||
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected iabInstance: InAppBrowserObject;
 | 
			
		||||
    protected uniqueIds: {[name: string]: number} = {};
 | 
			
		||||
    protected qrScanData: {deferred: PromiseDefer<any>, observable: Subscription};
 | 
			
		||||
    protected qrScanData: {deferred: PromiseDefer<string>; observable: Subscription};
 | 
			
		||||
 | 
			
		||||
    constructor(protected zone: NgZone) {
 | 
			
		||||
        this.logger = CoreLogger.getInstance('CoreUtilsProvider');
 | 
			
		||||
 | 
			
		||||
        Platform.instance.ready().then(() => {
 | 
			
		||||
            const win = <any> window;
 | 
			
		||||
 | 
			
		||||
            if (win.cordova && win.cordova.InAppBrowser) {
 | 
			
		||||
            if (window.cordova && window.cordova.InAppBrowser) {
 | 
			
		||||
                // Override the default window.open with the InAppBrowser one.
 | 
			
		||||
                win.open = win.cordova.InAppBrowser.open;
 | 
			
		||||
                window.open = window.cordova.InAppBrowser.open;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@ -60,7 +63,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param defaultError Message to show if the error is not a string.
 | 
			
		||||
     * @return New error message.
 | 
			
		||||
     */
 | 
			
		||||
    addDataNotDownloadedError(error: any, defaultError?: string): string {
 | 
			
		||||
    addDataNotDownloadedError(error: Error | string, defaultError?: string): string {
 | 
			
		||||
        let errorMessage = error;
 | 
			
		||||
 | 
			
		||||
        if (error && typeof error != 'string') {
 | 
			
		||||
@ -126,7 +129,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param result Object where to put the properties. If not defined, a new object will be created.
 | 
			
		||||
     * @return The object.
 | 
			
		||||
     */
 | 
			
		||||
    arrayToObject(array: any[], propertyName?: string, result?: any): any {
 | 
			
		||||
    arrayToObject(array: unknown[], propertyName?: string, result?: unknown): unknown {
 | 
			
		||||
        result = result || {};
 | 
			
		||||
        array.forEach((entry) => {
 | 
			
		||||
            const key = propertyName ? entry[propertyName] : entry;
 | 
			
		||||
@ -148,7 +151,9 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param undefinedIsNull True if undefined is equal to null. Defaults to true.
 | 
			
		||||
     * @return Whether both items are equal.
 | 
			
		||||
     */
 | 
			
		||||
    basicLeftCompare(itemA: any, itemB: any, maxLevels: number = 0, level: number = 0, undefinedIsNull: boolean = true): boolean {
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
 | 
			
		||||
    basicLeftCompare(itemA: any, itemB: any, maxLevels: number = 0,
 | 
			
		||||
            level: number = 0, undefinedIsNull: boolean = true): boolean {
 | 
			
		||||
        if (typeof itemA == 'function' || typeof itemB == 'function') {
 | 
			
		||||
            return true; // Don't compare functions.
 | 
			
		||||
        } else if (typeof itemA == 'object' && typeof itemB == 'object') {
 | 
			
		||||
@ -190,6 +195,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Blocks leaving a view.
 | 
			
		||||
     *
 | 
			
		||||
     * @deprecated, use ionViewCanLeave instead.
 | 
			
		||||
     */
 | 
			
		||||
    blockLeaveView(): void {
 | 
			
		||||
@ -202,35 +208,33 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param url The URL to check.
 | 
			
		||||
     * @return Promise resolved with boolean_ whether there is a redirect.
 | 
			
		||||
     */
 | 
			
		||||
    checkRedirect(url: string): Promise<boolean> {
 | 
			
		||||
        if (window.fetch) {
 | 
			
		||||
            const win = <any> window; // Convert to <any> to be able to use AbortController (not supported by our TS version).
 | 
			
		||||
            const initOptions: any = {
 | 
			
		||||
                redirect: 'follow',
 | 
			
		||||
            };
 | 
			
		||||
            let controller;
 | 
			
		||||
    async checkRedirect(url: string): Promise<boolean> {
 | 
			
		||||
        if (!window.fetch) {
 | 
			
		||||
            // Cannot check if there is a redirect, assume it's false.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Some browsers implement fetch but no AbortController.
 | 
			
		||||
            if (win.AbortController) {
 | 
			
		||||
                controller = new win.AbortController();
 | 
			
		||||
                initOptions.signal = controller.signal;
 | 
			
		||||
        const initOptions: RequestInit = { redirect: 'follow' };
 | 
			
		||||
 | 
			
		||||
        // Some browsers implement fetch but no AbortController.
 | 
			
		||||
        const controller = AbortController ? new AbortController() : false;
 | 
			
		||||
 | 
			
		||||
        if (controller) {
 | 
			
		||||
            initOptions.signal = controller.signal;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const response = await this.timeoutPromise(window.fetch(url, initOptions), CoreWS.instance.getRequestTimeout());
 | 
			
		||||
 | 
			
		||||
            return response.redirected;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error.timeout && controller) {
 | 
			
		||||
                // Timeout, abort the request.
 | 
			
		||||
                controller.abort();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return this.timeoutPromise(window.fetch(url, initOptions), CoreWS.instance.getRequestTimeout())
 | 
			
		||||
                    .then((response: Response) => {
 | 
			
		||||
                return response.redirected;
 | 
			
		||||
            }).catch((error) => {
 | 
			
		||||
                if (error.timeout && controller) {
 | 
			
		||||
                    // Timeout, abort the request.
 | 
			
		||||
                    controller.abort();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // There was a timeout, cannot determine if there's a redirect. Assume it's false.
 | 
			
		||||
                return false;
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            // Cannot check if there is a redirect, assume it's false.
 | 
			
		||||
            return Promise.resolve(false);
 | 
			
		||||
            // There was a timeout, cannot determine if there's a redirect. Assume it's false.
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -255,7 +259,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param level Depth we are right now inside a cloned object. It's used to prevent reaching max call stack size.
 | 
			
		||||
     * @return Cloned variable.
 | 
			
		||||
     */
 | 
			
		||||
    clone(source: any, level: number = 0): any {
 | 
			
		||||
    clone<T>(source: T, level: number = 0): T {
 | 
			
		||||
        if (level >= 20) {
 | 
			
		||||
            // Max 20 levels.
 | 
			
		||||
            this.logger.error('Max depth reached when cloning object.', source);
 | 
			
		||||
@ -265,7 +269,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
 | 
			
		||||
        if (Array.isArray(source)) {
 | 
			
		||||
            // Clone the array and all the entries.
 | 
			
		||||
            const newArray = [];
 | 
			
		||||
            const newArray = [] as unknown as T;
 | 
			
		||||
            for (let i = 0; i < source.length; i++) {
 | 
			
		||||
                newArray[i] = this.clone(source[i], level + 1);
 | 
			
		||||
            }
 | 
			
		||||
@ -279,7 +283,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Clone the object and all the subproperties.
 | 
			
		||||
            const newObject = {};
 | 
			
		||||
            const newObject = {} as T;
 | 
			
		||||
            for (const name in source) {
 | 
			
		||||
                newObject[name] = this.clone(source[name], level + 1);
 | 
			
		||||
            }
 | 
			
		||||
@ -298,7 +302,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param to Object where to store the properties.
 | 
			
		||||
     * @param clone Whether the properties should be cloned (so they are different instances).
 | 
			
		||||
     */
 | 
			
		||||
    copyProperties(from: any, to: any, clone: boolean = true): void {
 | 
			
		||||
    copyProperties(from: Record<string, unknown>, to: Record<string, unknown>, clone: boolean = true): void {
 | 
			
		||||
        for (const name in from) {
 | 
			
		||||
            if (clone) {
 | 
			
		||||
                to[name] = this.clone(from[name]);
 | 
			
		||||
@ -314,13 +318,15 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param text Text to be copied
 | 
			
		||||
     * @return Promise resolved when text is copied.
 | 
			
		||||
     */
 | 
			
		||||
    copyToClipboard(text: string): Promise<any> {
 | 
			
		||||
        return Clipboard.instance.copy(text).then(() => {
 | 
			
		||||
    async copyToClipboard(text: string): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await Clipboard.instance.copy(text);
 | 
			
		||||
 | 
			
		||||
            // Show toast using ionicLoading.
 | 
			
		||||
            return CoreDomUtils.instance.showToast('core.copiedtoclipboard', true);
 | 
			
		||||
        }).catch(() => {
 | 
			
		||||
            CoreDomUtils.instance.showToast('core.copiedtoclipboard', true);
 | 
			
		||||
        } catch {
 | 
			
		||||
            // Ignore errors.
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -339,7 +345,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @param array Array to empty.
 | 
			
		||||
     */
 | 
			
		||||
    emptyArray(array: any[]): void {
 | 
			
		||||
    emptyArray(array: unknown[]): void {
 | 
			
		||||
        array.length = 0; // Empty array without losing its reference.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -348,9 +354,9 @@ export class CoreUtilsProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @param object Object to remove the properties.
 | 
			
		||||
     */
 | 
			
		||||
    emptyObject(object: object): void {
 | 
			
		||||
    emptyObject(object: Record<string, unknown>): void {
 | 
			
		||||
        for (const key in object) {
 | 
			
		||||
            if (object.hasOwnProperty(key)) {
 | 
			
		||||
            if (Object.prototype.hasOwnProperty.call(object, key)) {
 | 
			
		||||
                delete object[key];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -373,10 +379,8 @@ export class CoreUtilsProvider {
 | 
			
		||||
        // Execute all the processes in order.
 | 
			
		||||
        for (const i in orderedPromisesData) {
 | 
			
		||||
            const data = orderedPromisesData[i];
 | 
			
		||||
            let promise;
 | 
			
		||||
 | 
			
		||||
            // Add the process to the dependency stack.
 | 
			
		||||
            promise = dependency.finally(() => {
 | 
			
		||||
            const promise = dependency.finally(() => {
 | 
			
		||||
                try {
 | 
			
		||||
                    return data.function();
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
@ -406,19 +410,19 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param useDotNotation Whether to use dot notation '.' or square brackets '['.
 | 
			
		||||
     * @return Flattened object.
 | 
			
		||||
     */
 | 
			
		||||
    flattenObject(obj: object, useDotNotation?: boolean): object {
 | 
			
		||||
    flattenObject(obj: Record<string, unknown>, useDotNotation?: boolean): Record<string, unknown> {
 | 
			
		||||
        const toReturn = {};
 | 
			
		||||
 | 
			
		||||
        for (const name in obj) {
 | 
			
		||||
            if (!obj.hasOwnProperty(name)) {
 | 
			
		||||
            if (!Object.prototype.hasOwnProperty.call(obj, name)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const value = obj[name];
 | 
			
		||||
            if (typeof value == 'object' && !Array.isArray(value)) {
 | 
			
		||||
                const flatObject = this.flattenObject(value);
 | 
			
		||||
                const flatObject = this.flattenObject(value as Record<string, unknown>);
 | 
			
		||||
                for (const subName in flatObject) {
 | 
			
		||||
                    if (!flatObject.hasOwnProperty(subName)) {
 | 
			
		||||
                    if (!Object.prototype.hasOwnProperty.call(flatObject, subName)) {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@ -463,8 +467,8 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param ...args All the params sent after checkAll will be passed to isEnabledFn.
 | 
			
		||||
     * @return Promise resolved with the list of enabled sites.
 | 
			
		||||
     */
 | 
			
		||||
    filterEnabledSites(siteIds: string[], isEnabledFn: (siteId, ...args: any[]) => boolean | Promise<boolean>, checkAll?: boolean,
 | 
			
		||||
            ...args: any[]): Promise<string[]> {
 | 
			
		||||
    filterEnabledSites<P extends []>(siteIds: string[], isEnabledFn: (siteId, ...args: P) => boolean | Promise<boolean>,
 | 
			
		||||
            checkAll?: boolean, ...args: P): Promise<string[]> {
 | 
			
		||||
        const promises = [];
 | 
			
		||||
        const enabledSites = [];
 | 
			
		||||
 | 
			
		||||
@ -498,7 +502,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param float The float to print.
 | 
			
		||||
     * @return Locale float.
 | 
			
		||||
     */
 | 
			
		||||
    formatFloat(float: any): string {
 | 
			
		||||
    formatFloat(float: unknown): string {
 | 
			
		||||
        if (typeof float == 'undefined' || float === null || typeof float == 'boolean') {
 | 
			
		||||
            return '';
 | 
			
		||||
        }
 | 
			
		||||
@ -506,9 +510,9 @@ export class CoreUtilsProvider {
 | 
			
		||||
        const localeSeparator = Translate.instance.instant('core.decsep');
 | 
			
		||||
 | 
			
		||||
        // Convert float to string.
 | 
			
		||||
        float += '';
 | 
			
		||||
        const floatString = float + '';
 | 
			
		||||
 | 
			
		||||
        return float.replace('.', localeSeparator);
 | 
			
		||||
        return floatString.replace('.', localeSeparator);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -523,17 +527,15 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param maxDepth Max Depth to convert to tree. Children found will be in the last level of depth.
 | 
			
		||||
     * @return Array with the formatted tree, children will be on each node under children field.
 | 
			
		||||
     */
 | 
			
		||||
    formatTree(list: any[], parentFieldName: string = 'parent', idFieldName: string = 'id', rootParentId: number = 0,
 | 
			
		||||
            maxDepth: number = 5): any[] {
 | 
			
		||||
    formatTree<T>(list: T[], parentFieldName: string = 'parent', idFieldName: string = 'id', rootParentId: number = 0,
 | 
			
		||||
            maxDepth: number = 5): TreeNode<T>[] {
 | 
			
		||||
        const map = {};
 | 
			
		||||
        const mapDepth = {};
 | 
			
		||||
        const tree = [];
 | 
			
		||||
        let parent;
 | 
			
		||||
        let id;
 | 
			
		||||
        const tree: TreeNode<T>[] = [];
 | 
			
		||||
 | 
			
		||||
        list.forEach((node, index): void => {
 | 
			
		||||
            id = node[idFieldName];
 | 
			
		||||
            parent = node[parentFieldName];
 | 
			
		||||
        list.forEach((node: TreeNode<T>, index): void => {
 | 
			
		||||
            const id = node[idFieldName];
 | 
			
		||||
            const parent = node[parentFieldName];
 | 
			
		||||
            node.children = [];
 | 
			
		||||
 | 
			
		||||
            if (!id || !parent) {
 | 
			
		||||
@ -543,7 +545,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
            // Use map to look-up the parents.
 | 
			
		||||
            map[id] = index;
 | 
			
		||||
            if (parent != rootParentId) {
 | 
			
		||||
                const parentNode = list[map[parent]];
 | 
			
		||||
                const parentNode = list[map[parent]] as TreeNode<T>;
 | 
			
		||||
                if (parentNode) {
 | 
			
		||||
                    if (mapDepth[parent] == maxDepth) {
 | 
			
		||||
                        // Reached max level of depth. Proceed with flat order. Find parent object of the current node.
 | 
			
		||||
@ -551,11 +553,11 @@ export class CoreUtilsProvider {
 | 
			
		||||
                        if (parentOfParent) {
 | 
			
		||||
                            // This element will be the child of the node that is two levels up the hierarchy
 | 
			
		||||
                            // (i.e. the child of node.parent.parent).
 | 
			
		||||
                            list[map[parentOfParent]].children.push(node);
 | 
			
		||||
                            (list[map[parentOfParent]] as TreeNode<T>).children.push(node);
 | 
			
		||||
                            // Assign depth level to the same depth as the parent (i.e. max depth level).
 | 
			
		||||
                            mapDepth[id] = mapDepth[parent];
 | 
			
		||||
                            // Change the parent to be the one that is two levels up the hierarchy.
 | 
			
		||||
                            node.parent = parentOfParent;
 | 
			
		||||
                            node[parentFieldName] = parentOfParent;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            this.logger.error(`Node parent of parent:${parentOfParent} not found on formatTree`);
 | 
			
		||||
                        }
 | 
			
		||||
@ -596,7 +598,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved with the list of countries.
 | 
			
		||||
     */
 | 
			
		||||
    getCountryList(): Promise<any> {
 | 
			
		||||
    getCountryList(): Promise<Record<string, string>> {
 | 
			
		||||
        // Get the keys of the countries.
 | 
			
		||||
        return this.getCountryKeysList().then((keys) => {
 | 
			
		||||
            // Now get the code and the translated name.
 | 
			
		||||
@ -618,14 +620,14 @@ export class CoreUtilsProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved with the list of countries.
 | 
			
		||||
     */
 | 
			
		||||
    getCountryListSorted(): Promise<any[]> {
 | 
			
		||||
    getCountryListSorted(): Promise<{ code: string; name: string }[]> {
 | 
			
		||||
        // Get the keys of the countries.
 | 
			
		||||
        return this.getCountryList().then((countries) => {
 | 
			
		||||
            // Sort translations.
 | 
			
		||||
            const sortedCountries = [];
 | 
			
		||||
 | 
			
		||||
            Object.keys(countries).sort((a, b) => countries[a].localeCompare(countries[b])).forEach((key) => {
 | 
			
		||||
                sortedCountries.push({code: key, name: countries[key]});
 | 
			
		||||
                sortedCountries.push({ code: key, name: countries[key] });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return sortedCountries;
 | 
			
		||||
@ -660,25 +662,25 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param lang Language to check.
 | 
			
		||||
     * @return Promise resolved with the countries list. Rejected if not translated.
 | 
			
		||||
     */
 | 
			
		||||
    protected getCountryKeysListForLanguage(lang: string): Promise<string[]> {
 | 
			
		||||
    protected async getCountryKeysListForLanguage(lang: string): Promise<string[]> {
 | 
			
		||||
        // Get the translation table for the language.
 | 
			
		||||
        return CoreLang.instance.getTranslationTable(lang).then((table): any => {
 | 
			
		||||
            // Gather all the keys for countries,
 | 
			
		||||
            const keys = [];
 | 
			
		||||
        const table = await CoreLang.instance.getTranslationTable(lang);
 | 
			
		||||
 | 
			
		||||
            for (const name in table) {
 | 
			
		||||
                if (name.indexOf('assets.countries.') === 0) {
 | 
			
		||||
                    keys.push(name);
 | 
			
		||||
                }
 | 
			
		||||
        // Gather all the keys for countries,
 | 
			
		||||
        const keys = [];
 | 
			
		||||
 | 
			
		||||
        for (const name in table) {
 | 
			
		||||
            if (name.indexOf('assets.countries.') === 0) {
 | 
			
		||||
                keys.push(name);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            if (keys.length === 0) {
 | 
			
		||||
                // Not translated, reject.
 | 
			
		||||
                return Promise.reject('Countries not found.');
 | 
			
		||||
            }
 | 
			
		||||
        if (keys.length === 0) {
 | 
			
		||||
            // Not translated, reject.
 | 
			
		||||
            throw new Error('Countries not found.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            return keys;
 | 
			
		||||
        });
 | 
			
		||||
        return keys;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -699,9 +701,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Can't be guessed, get the remote mimetype.
 | 
			
		||||
        return CoreWS.instance.getRemoteFileMimeType(url).then((mimetype) => {
 | 
			
		||||
            return mimetype || '';
 | 
			
		||||
        });
 | 
			
		||||
        return CoreWS.instance.getRemoteFileMimeType(url).then(mimetype => mimetype || '');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -718,13 +718,23 @@ export class CoreUtilsProvider {
 | 
			
		||||
        return ++this.uniqueIds[name];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a file is a FileEntry
 | 
			
		||||
     *
 | 
			
		||||
     * @param file File.
 | 
			
		||||
     * @return Type guard indicating if the file is a FileEntry.
 | 
			
		||||
     */
 | 
			
		||||
    isFileEntry(file: FileEntry | CoreWSExternalFile): file is FileEntry {
 | 
			
		||||
        return 'isFile' in file;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a list of files, check if there are repeated names.
 | 
			
		||||
     *
 | 
			
		||||
     * @param files List of files.
 | 
			
		||||
     * @return String with error message if repeated, false if no repeated.
 | 
			
		||||
     */
 | 
			
		||||
    hasRepeatedFilenames(files: any[]): string | boolean {
 | 
			
		||||
    hasRepeatedFilenames(files: (FileEntry | CoreWSExternalFile)[]): string | false {
 | 
			
		||||
        if (!files || !files.length) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
@ -733,12 +743,14 @@ export class CoreUtilsProvider {
 | 
			
		||||
 | 
			
		||||
        // Check if there are 2 files with the same name.
 | 
			
		||||
        for (let i = 0; i < files.length; i++) {
 | 
			
		||||
            const name = files[i].filename || files[i].name;
 | 
			
		||||
            const file = files[i];
 | 
			
		||||
            const name = this.isFileEntry(file) ? file.name : file.filename;
 | 
			
		||||
 | 
			
		||||
            if (names.indexOf(name) > -1) {
 | 
			
		||||
                return Translate.instance.instant('core.filenameexist', { $a: name });
 | 
			
		||||
            } else {
 | 
			
		||||
                names.push(name);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            names.push(name);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
@ -774,6 +786,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param value Value to check.
 | 
			
		||||
     * @return Whether the value is false, 0 or "0".
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
 | 
			
		||||
    isFalseOrZero(value: any): boolean {
 | 
			
		||||
        return typeof value != 'undefined' && (value === false || value === 'false' || parseInt(value, 10) === 0);
 | 
			
		||||
    }
 | 
			
		||||
@ -784,6 +797,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param value Value to check.
 | 
			
		||||
     * @return Whether the value is true, 1 or "1".
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
 | 
			
		||||
    isTrueOrOne(value: any): boolean {
 | 
			
		||||
        return typeof value != 'undefined' && (value === true || value === 'true' || parseInt(value, 10) === 1);
 | 
			
		||||
    }
 | 
			
		||||
@ -794,6 +808,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param error Error to check.
 | 
			
		||||
     * @return Whether the error was returned by the WebService.
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
 | 
			
		||||
    isWebServiceError(error: any): boolean {
 | 
			
		||||
        return error && (typeof error.warningcode != 'undefined' || (typeof error.errorcode != 'undefined' &&
 | 
			
		||||
                error.errorcode != 'invalidtoken' && error.errorcode != 'userdeleted' && error.errorcode != 'upgraderunning' &&
 | 
			
		||||
@ -812,19 +827,18 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param defaultValue Element that will become default option value. Default 0.
 | 
			
		||||
     * @return The now assembled array
 | 
			
		||||
     */
 | 
			
		||||
    makeMenuFromList(list: string, defaultLabel?: string, separator: string = ',', defaultValue?: any): any[] {
 | 
			
		||||
    makeMenuFromList<T>(list: string, defaultLabel?: string, separator: string = ',',
 | 
			
		||||
            defaultValue?: T): { label: string; value: T | number }[] {
 | 
			
		||||
        // Split and format the list.
 | 
			
		||||
        const split = list.split(separator).map((label, index) => {
 | 
			
		||||
            return {
 | 
			
		||||
                label: label.trim(),
 | 
			
		||||
                value: index + 1
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
        const split = list.split(separator).map((label, index) => ({
 | 
			
		||||
            label: label.trim(),
 | 
			
		||||
            value: index + 1,
 | 
			
		||||
        })) as { label: string; value: T | number }[];
 | 
			
		||||
 | 
			
		||||
        if (defaultLabel) {
 | 
			
		||||
            split.unshift({
 | 
			
		||||
                label: defaultLabel,
 | 
			
		||||
                value: defaultValue || 0
 | 
			
		||||
                value: defaultValue || 0,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -839,8 +853,8 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param [key] Key of the property that must be unique. If not specified, the whole entry.
 | 
			
		||||
     * @return Merged array.
 | 
			
		||||
     */
 | 
			
		||||
    mergeArraysWithoutDuplicates(array1: any[], array2: any[], key?: string): any[] {
 | 
			
		||||
        return this.uniqueArray(array1.concat(array2), key);
 | 
			
		||||
    mergeArraysWithoutDuplicates<T>(array1: T[], array2: T[], key?: string): T[] {
 | 
			
		||||
        return this.uniqueArray(array1.concat(array2), key) as T[];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -849,6 +863,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param value Value to check.
 | 
			
		||||
     * @return True if not null and not undefined.
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
 | 
			
		||||
    notNullOrUndefined(value: any): boolean {
 | 
			
		||||
        return typeof value != 'undefined' && value !== null;
 | 
			
		||||
    }
 | 
			
		||||
@ -888,12 +903,10 @@ export class CoreUtilsProvider {
 | 
			
		||||
 | 
			
		||||
            if (!extension || extension.indexOf('/') > -1 || extension.indexOf('\\') > -1) {
 | 
			
		||||
                // Extension not found.
 | 
			
		||||
                error = Translate.instance.instant('core.erroropenfilenoextension');
 | 
			
		||||
            } else {
 | 
			
		||||
                error = Translate.instance.instant('core.erroropenfilenoapp');
 | 
			
		||||
                throw new Error(Translate.instance.instant('core.erroropenfilenoextension'));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw error;
 | 
			
		||||
            throw new Error(Translate.instance.instant('core.erroropenfilenoapp'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -905,7 +918,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param options Override default options passed to InAppBrowser.
 | 
			
		||||
     * @return The opened window.
 | 
			
		||||
     */
 | 
			
		||||
    openInApp(url: string, options?: any): InAppBrowserObject {
 | 
			
		||||
    openInApp(url: string, options?: InAppBrowserOptions): InAppBrowserObject {
 | 
			
		||||
        if (!url) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -1009,7 +1022,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
                const options = {
 | 
			
		||||
                    action: WebIntent.instance.ACTION_VIEW,
 | 
			
		||||
                    url,
 | 
			
		||||
                    type: mimetype
 | 
			
		||||
                    type: mimetype,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                return WebIntent.instance.startActivity(options).catch((error) => {
 | 
			
		||||
@ -1033,10 +1046,8 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param obj Object to convert.
 | 
			
		||||
     * @return Array with the values of the object but losing the keys.
 | 
			
		||||
     */
 | 
			
		||||
    objectToArray(obj: object): any[] {
 | 
			
		||||
        return Object.keys(obj).map((key) => {
 | 
			
		||||
            return obj[key];
 | 
			
		||||
        });
 | 
			
		||||
    objectToArray<T>(obj: Record<string, T>): T[] {
 | 
			
		||||
        return Object.keys(obj).map((key) => obj[key]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1051,9 +1062,10 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param sortByValue True to sort values alphabetically, false otherwise.
 | 
			
		||||
     * @return Array of objects with the name & value of each property.
 | 
			
		||||
     */
 | 
			
		||||
    objectToArrayOfObjects(obj: object, keyName: string, valueName: string, sortByKey?: boolean, sortByValue?: boolean): any[] {
 | 
			
		||||
    objectToArrayOfObjects(obj: Record<string, unknown>, keyName: string, valueName: string, sortByKey?: boolean,
 | 
			
		||||
            sortByValue?: boolean): Record<string, unknown>[] {
 | 
			
		||||
        // Get the entries from an object or primitive value.
 | 
			
		||||
        const getEntries = (elKey, value): any[] | any => {
 | 
			
		||||
        const getEntries = (elKey: string, value: unknown): Record<string, unknown>[] | unknown => {
 | 
			
		||||
            if (typeof value == 'undefined' || value == null) {
 | 
			
		||||
                // Filter undefined and null values.
 | 
			
		||||
                return;
 | 
			
		||||
@ -1087,7 +1099,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // "obj" will always be an object, so "entries" will always be an array.
 | 
			
		||||
        const entries = <any[]> getEntries('', obj);
 | 
			
		||||
        const entries = getEntries('', obj) as Record<string, unknown>[];
 | 
			
		||||
        if (sortByKey || sortByValue) {
 | 
			
		||||
            return entries.sort((a, b) => {
 | 
			
		||||
                if (sortByKey) {
 | 
			
		||||
@ -1111,7 +1123,8 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param keyPrefix Key prefix if neededs to delete it.
 | 
			
		||||
     * @return Object.
 | 
			
		||||
     */
 | 
			
		||||
    objectToKeyValueMap(objects: object[], keyName: string, valueName: string, keyPrefix?: string): {[name: string]: any} {
 | 
			
		||||
    objectToKeyValueMap(objects: Record<string, unknown>[], keyName: string, valueName: string,
 | 
			
		||||
            keyPrefix?: string): {[name: string]: unknown} {
 | 
			
		||||
        if (!objects) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -1119,7 +1132,8 @@ export class CoreUtilsProvider {
 | 
			
		||||
        const prefixSubstr = keyPrefix ? keyPrefix.length : 0;
 | 
			
		||||
        const mapped = {};
 | 
			
		||||
        objects.forEach((item) => {
 | 
			
		||||
            const key = prefixSubstr > 0 ? item[keyName].substr(prefixSubstr) : item[keyName];
 | 
			
		||||
            const keyValue = item[keyName] as string;
 | 
			
		||||
            const key = prefixSubstr > 0 ? keyValue.substr(prefixSubstr) : keyValue;
 | 
			
		||||
            mapped[key] = item[valueName];
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -1133,7 +1147,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param removeEmpty Whether to remove params whose value is null/undefined.
 | 
			
		||||
     * @return GET params.
 | 
			
		||||
     */
 | 
			
		||||
    objectToGetParams(object: any, removeEmpty: boolean = true): string {
 | 
			
		||||
    objectToGetParams(object: Record<string, unknown>, removeEmpty: boolean = true): string {
 | 
			
		||||
        // First of all, flatten the object so all properties are in the first level.
 | 
			
		||||
        const flattened = this.flattenObject(object);
 | 
			
		||||
        let result = '';
 | 
			
		||||
@ -1164,7 +1178,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param prefix Prefix to add.
 | 
			
		||||
     * @return Prefixed object.
 | 
			
		||||
     */
 | 
			
		||||
    prefixKeys(data: any, prefix: string): any {
 | 
			
		||||
    prefixKeys(data: Record<string, unknown>, prefix: string): Record<string, unknown> {
 | 
			
		||||
        const newObj = {};
 | 
			
		||||
        const keys = Object.keys(data);
 | 
			
		||||
 | 
			
		||||
@ -1196,12 +1210,14 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param promise Promise to check
 | 
			
		||||
     * @return Promise resolved with boolean: true if the promise is rejected or false if it's resolved.
 | 
			
		||||
     */
 | 
			
		||||
    promiseFails(promise: Promise<any>): Promise<boolean> {
 | 
			
		||||
        return promise.then(() => {
 | 
			
		||||
    async promiseFails(promise: Promise<unknown>): Promise<boolean> {
 | 
			
		||||
        try {
 | 
			
		||||
            await promise;
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }).catch(() => {
 | 
			
		||||
        } catch {
 | 
			
		||||
            return true;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1210,12 +1226,14 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param promise Promise to check
 | 
			
		||||
     * @return Promise resolved with boolean: true if the promise it's resolved or false if it's rejected.
 | 
			
		||||
     */
 | 
			
		||||
    promiseWorks(promise: Promise<any>): Promise<boolean> {
 | 
			
		||||
        return promise.then(() => {
 | 
			
		||||
    async promiseWorks(promise: Promise<unknown>): Promise<boolean> {
 | 
			
		||||
        try {
 | 
			
		||||
            await promise;
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }).catch(() => {
 | 
			
		||||
        } catch {
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1228,7 +1246,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param key Key to check.
 | 
			
		||||
     * @return Whether the two objects/arrays have the same value (or lack of one) for a given key.
 | 
			
		||||
     */
 | 
			
		||||
    sameAtKeyMissingIsBlank(obj1: any, obj2: any, key: string): boolean {
 | 
			
		||||
    sameAtKeyMissingIsBlank(obj1: unknown, obj2: unknown, key: string): boolean {
 | 
			
		||||
        let value1 = typeof obj1[key] != 'undefined' ? obj1[key] : '';
 | 
			
		||||
        let value2 = typeof obj2[key] != 'undefined' ? obj2[key] : '';
 | 
			
		||||
 | 
			
		||||
@ -1249,7 +1267,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param obj Object to stringify.
 | 
			
		||||
     * @return Stringified object.
 | 
			
		||||
     */
 | 
			
		||||
    sortAndStringify(obj: object): string {
 | 
			
		||||
    sortAndStringify(obj: Record<string, unknown>): string {
 | 
			
		||||
        return JSON.stringify(this.sortProperties(obj));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1259,7 +1277,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param obj The object to sort. If it isn't an object, the original value will be returned.
 | 
			
		||||
     * @return Sorted object.
 | 
			
		||||
     */
 | 
			
		||||
    sortProperties(obj: object): object {
 | 
			
		||||
    sortProperties<T>(obj: T): T {
 | 
			
		||||
        if (obj != null && typeof obj == 'object' && !Array.isArray(obj)) {
 | 
			
		||||
            // It's an object, sort it.
 | 
			
		||||
            return Object.keys(obj).sort().reduce((accumulator, key) => {
 | 
			
		||||
@ -1267,7 +1285,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
                accumulator[key] = this.sortProperties(obj[key]);
 | 
			
		||||
 | 
			
		||||
                return accumulator;
 | 
			
		||||
            }, {});
 | 
			
		||||
            }, {} as T);
 | 
			
		||||
        } else {
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
@ -1279,12 +1297,12 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param obj The object to sort. If it isn't an object, the original value will be returned.
 | 
			
		||||
     * @return Sorted object.
 | 
			
		||||
     */
 | 
			
		||||
    sortValues(obj: object): object {
 | 
			
		||||
    sortValues<T>(obj: T): T {
 | 
			
		||||
        if (typeof obj == 'object' && !Array.isArray(obj)) {
 | 
			
		||||
            // It's an object, sort it. Convert it to an array to be able to sort it and then convert it back to object.
 | 
			
		||||
            const array = this.objectToArrayOfObjects(obj, 'name', 'value', false, true);
 | 
			
		||||
            const array = this.objectToArrayOfObjects(obj as Record<string, unknown>, 'name', 'value', false, true);
 | 
			
		||||
 | 
			
		||||
            return this.objectToKeyValueMap(array, 'name', 'value');
 | 
			
		||||
            return this.objectToKeyValueMap(array, 'name', 'value') as unknown as T;
 | 
			
		||||
        } else {
 | 
			
		||||
            return obj;
 | 
			
		||||
        }
 | 
			
		||||
@ -1297,10 +1315,10 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @return File size and a boolean to indicate if it is the total size or only partial.
 | 
			
		||||
     * @deprecated since 3.8.0. Use CorePluginFileDelegate.getFilesSize instead.
 | 
			
		||||
     */
 | 
			
		||||
    sumFileSizes(files: any[]): { size: number, total: boolean } {
 | 
			
		||||
    sumFileSizes(files: CoreWSExternalFile[]): { size: number; total: boolean } {
 | 
			
		||||
        const result = {
 | 
			
		||||
            size: 0,
 | 
			
		||||
            total: true
 | 
			
		||||
            total: true,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        files.forEach((file) => {
 | 
			
		||||
@ -1326,7 +1344,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
    timeoutPromise<T>(promise: Promise<T>, time: number): Promise<T> {
 | 
			
		||||
        return new Promise((resolve, reject): void => {
 | 
			
		||||
            const timeout = setTimeout(() => {
 | 
			
		||||
                reject({timeout: true});
 | 
			
		||||
                reject({ timeout: true });
 | 
			
		||||
            }, time);
 | 
			
		||||
 | 
			
		||||
            promise.then(resolve).catch(reject).finally(() => {
 | 
			
		||||
@ -1344,7 +1362,8 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param strict If true, then check the input and return false if it is not a valid number.
 | 
			
		||||
     * @return False if bad format, empty string if empty value or the parsed float if not.
 | 
			
		||||
     */
 | 
			
		||||
    unformatFloat(localeFloat: any, strict?: boolean): any {
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
 | 
			
		||||
    unformatFloat(localeFloat: any, strict?: boolean): false | '' | number {
 | 
			
		||||
        // Bad format on input type number.
 | 
			
		||||
        if (typeof localeFloat == 'undefined') {
 | 
			
		||||
            return false;
 | 
			
		||||
@ -1383,7 +1402,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param [key] Key of the property that must be unique. If not specified, the whole entry.
 | 
			
		||||
     * @return Array without duplicate values.
 | 
			
		||||
     */
 | 
			
		||||
    uniqueArray(array: any[], key?: string): any[] {
 | 
			
		||||
    uniqueArray<T>(array: T[], key?: string): T[] {
 | 
			
		||||
        const filtered = [];
 | 
			
		||||
        const unique = {}; // Use an object to make it faster to check if it's duplicate.
 | 
			
		||||
 | 
			
		||||
@ -1407,16 +1426,13 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param delay Time that must pass until the function is called.
 | 
			
		||||
     * @return Debounced function.
 | 
			
		||||
     */
 | 
			
		||||
    debounce(fn: (...args: any[]) => any, delay: number): (...args: any[]) => void {
 | 
			
		||||
 | 
			
		||||
    debounce<T extends unknown[]>(fn: (...args: T) => unknown, delay: number): (...args: T) => void {
 | 
			
		||||
        let timeoutID: number;
 | 
			
		||||
 | 
			
		||||
        const debounced = (...args: any[]): void => {
 | 
			
		||||
        const debounced = (...args: unknown[]): void => {
 | 
			
		||||
            clearTimeout(timeoutID);
 | 
			
		||||
 | 
			
		||||
            timeoutID = window.setTimeout(() => {
 | 
			
		||||
                fn.apply(null, args);
 | 
			
		||||
            }, delay);
 | 
			
		||||
            timeoutID = window.setTimeout(() => fn.apply(null, args), delay);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return debounced;
 | 
			
		||||
@ -1448,18 +1464,19 @@ export class CoreUtilsProvider {
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved with the QR string, rejected if error or cancelled.
 | 
			
		||||
     */
 | 
			
		||||
    startScanQR(): Promise<string> {
 | 
			
		||||
    async startScanQR(): Promise<string> {
 | 
			
		||||
        if (!CoreApp.instance.isMobile()) {
 | 
			
		||||
            return Promise.reject('QRScanner isn\'t available in desktop apps.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Ask the user for permission to use the camera.
 | 
			
		||||
        // The scan method also does this, but since it returns an Observable we wouldn't be able to detect if the user denied.
 | 
			
		||||
        return QRScanner.instance.prepare().then((status) => {
 | 
			
		||||
        try {
 | 
			
		||||
            const status = await QRScanner.instance.prepare();
 | 
			
		||||
 | 
			
		||||
            if (!status.authorized) {
 | 
			
		||||
                // No access to the camera, reject. In android this shouldn't happen, denying access passes through catch.
 | 
			
		||||
                return Promise.reject('The user denied camera access.');
 | 
			
		||||
                throw new Error('The user denied camera access.');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.qrScanData && this.qrScanData.deferred) {
 | 
			
		||||
@ -1470,29 +1487,29 @@ export class CoreUtilsProvider {
 | 
			
		||||
            // Start scanning.
 | 
			
		||||
            this.qrScanData = {
 | 
			
		||||
                deferred: this.promiseDefer(),
 | 
			
		||||
                observable: QRScanner.instance.scan().subscribe((text) => {
 | 
			
		||||
 | 
			
		||||
                    // Text received, stop scanning and return the text.
 | 
			
		||||
                    this.stopScanQR(text, false);
 | 
			
		||||
                })
 | 
			
		||||
                // When text is received, stop scanning and return the text.
 | 
			
		||||
                observable: QRScanner.instance.scan().subscribe(text => this.stopScanQR(text, false)),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Show the camera.
 | 
			
		||||
            return QRScanner.instance.show().then(() => {
 | 
			
		||||
            try {
 | 
			
		||||
                await QRScanner.instance.show();
 | 
			
		||||
 | 
			
		||||
                document.body.classList.add('core-scanning-qr');
 | 
			
		||||
 | 
			
		||||
                return this.qrScanData.deferred.promise;
 | 
			
		||||
            }, (err) => {
 | 
			
		||||
                this.stopScanQR(err, true);
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                this.stopScanQR(e, true);
 | 
			
		||||
 | 
			
		||||
                return Promise.reject(err);
 | 
			
		||||
            });
 | 
			
		||||
                throw e;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // eslint-disable-next-line no-underscore-dangle, @typescript-eslint/naming-convention
 | 
			
		||||
            error.message = error.message || (error as { _message?: string })._message;
 | 
			
		||||
 | 
			
		||||
        }).catch((err) => {
 | 
			
		||||
            err.message = err.message || err._message;
 | 
			
		||||
 | 
			
		||||
            return Promise.reject(err);
 | 
			
		||||
        });
 | 
			
		||||
            throw error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -1501,8 +1518,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @param data If success, the text of the QR code. If error, the error object or message. Undefined for cancelled.
 | 
			
		||||
     * @param error True if the data belongs to an error, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    stopScanQR(data?: any, error?: boolean): void {
 | 
			
		||||
 | 
			
		||||
    stopScanQR(data?: string | Error, error?: boolean): void {
 | 
			
		||||
        if (!this.qrScanData) {
 | 
			
		||||
            // Not scanning.
 | 
			
		||||
            return;
 | 
			
		||||
@ -1518,7 +1534,7 @@ export class CoreUtilsProvider {
 | 
			
		||||
        if (error) {
 | 
			
		||||
            this.qrScanData.deferred.reject(data);
 | 
			
		||||
        } else if (typeof data != 'undefined') {
 | 
			
		||||
            this.qrScanData.deferred.resolve(data);
 | 
			
		||||
            this.qrScanData.deferred.resolve(data as string);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.qrScanData.deferred.reject(CoreDomUtils.instance.createCanceledError());
 | 
			
		||||
        }
 | 
			
		||||
@ -1549,10 +1565,9 @@ export class CoreUtilsProvider {
 | 
			
		||||
     * @return Promise resolved after the time has passed.
 | 
			
		||||
     */
 | 
			
		||||
    wait(milliseconds: number): Promise<void> {
 | 
			
		||||
        return new Promise((resolve, reject): void => {
 | 
			
		||||
            setTimeout(resolve, milliseconds);
 | 
			
		||||
        });
 | 
			
		||||
        return new Promise(resolve => setTimeout(resolve, milliseconds));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class CoreUtils extends makeSingleton(CoreUtilsProvider) {}
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,9 @@
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "outDir": "./out-tsc/app",
 | 
			
		||||
    "types": [
 | 
			
		||||
      "cordova",
 | 
			
		||||
      "cordova-plugin-file-transfer",
 | 
			
		||||
      "cordova-plugin-inappbrowser",
 | 
			
		||||
      "cordova",
 | 
			
		||||
      "node"
 | 
			
		||||
    ],
 | 
			
		||||
    "paths": {
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,9 @@
 | 
			
		||||
      "dom"
 | 
			
		||||
    ],
 | 
			
		||||
    "types": [
 | 
			
		||||
        "cordova-plugin-file-transfer",
 | 
			
		||||
        "cordova-plugin-inappbrowser",
 | 
			
		||||
        "cordova",
 | 
			
		||||
        "jest",
 | 
			
		||||
        "node"
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,9 @@
 | 
			
		||||
    "emitDecoratorMetadata": true,
 | 
			
		||||
    "outDir": "./out-tsc/tests",
 | 
			
		||||
    "types": [
 | 
			
		||||
      "cordova-plugin-file-transfer",
 | 
			
		||||
      "cordova-plugin-inappbrowser",
 | 
			
		||||
      "cordova",
 | 
			
		||||
      "jest",
 | 
			
		||||
      "node"
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user