commit
b1e637812e
|
@ -148,6 +148,7 @@ var appConfig = {
|
||||||
'warn',
|
'warn',
|
||||||
{
|
{
|
||||||
allowFinally: true,
|
allowFinally: true,
|
||||||
|
terminationMethod: ['catch', 'finally'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'arrow-body-style': ['error', 'as-needed'],
|
'arrow-body-style': ['error', 'as-needed'],
|
||||||
|
|
|
@ -40,7 +40,7 @@ export class CoreSingletonsFactory {
|
||||||
/**
|
/**
|
||||||
* Angular injector used to resolve singleton instances.
|
* Angular injector used to resolve singleton instances.
|
||||||
*/
|
*/
|
||||||
private injector: Injector;
|
private injector?: Injector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the injector that will be used to resolve instances in the singletons created with this factory.
|
* Set the injector that will be used to resolve instances in the singletons created with this factory.
|
||||||
|
@ -68,6 +68,10 @@ export class CoreSingletonsFactory {
|
||||||
static get instance(): Service {
|
static get instance(): Service {
|
||||||
// Initialize instances lazily.
|
// Initialize instances lazily.
|
||||||
if (!this.serviceInstance) {
|
if (!this.serviceInstance) {
|
||||||
|
if (!factory.injector) {
|
||||||
|
throw new Error('Can\'t resolve a singleton instance without an injector');
|
||||||
|
}
|
||||||
|
|
||||||
this.serviceInstance = factory.injector.get(injectionToken);
|
this.serviceInstance = factory.injector.get(injectionToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,8 +133,8 @@ export interface SQLiteDBForeignKeySchema {
|
||||||
*/
|
*/
|
||||||
export class SQLiteDB {
|
export class SQLiteDB {
|
||||||
|
|
||||||
db: SQLiteObject;
|
db?: SQLiteObject;
|
||||||
promise: Promise<void>;
|
promise!: Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and open the database.
|
* Create and open the database.
|
||||||
|
@ -164,7 +164,7 @@ export class SQLiteDB {
|
||||||
foreignKeys?: SQLiteDBForeignKeySchema[],
|
foreignKeys?: SQLiteDBForeignKeySchema[],
|
||||||
tableCheck?: string,
|
tableCheck?: string,
|
||||||
): string {
|
): string {
|
||||||
const columnsSql = [];
|
const columnsSql: string[] = [];
|
||||||
let sql = `CREATE TABLE IF NOT EXISTS ${name} (`;
|
let sql = `CREATE TABLE IF NOT EXISTS ${name} (`;
|
||||||
|
|
||||||
// First define all the columns.
|
// First define all the columns.
|
||||||
|
@ -225,8 +225,8 @@ export class SQLiteDB {
|
||||||
for (const index in foreignKeys) {
|
for (const index in foreignKeys) {
|
||||||
const foreignKey = foreignKeys[index];
|
const foreignKey = foreignKeys[index];
|
||||||
|
|
||||||
if (!foreignKey.columns || !!foreignKey.columns.length) {
|
if (!foreignKey?.columns.length) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
sql += `, FOREIGN KEY (${foreignKey.columns.join(', ')}) REFERENCES ${foreignKey.table} `;
|
sql += `, FOREIGN KEY (${foreignKey.columns.join(', ')}) REFERENCES ${foreignKey.table} `;
|
||||||
|
@ -251,7 +251,7 @@ export class SQLiteDB {
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
await this.ready();
|
await this.ready();
|
||||||
|
|
||||||
await this.db.close();
|
await this.db!.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -348,10 +348,7 @@ export class SQLiteDB {
|
||||||
* @return Promise resolved when success.
|
* @return Promise resolved when success.
|
||||||
*/
|
*/
|
||||||
async createTablesFromSchema(tables: SQLiteDBTableSchema[]): Promise<void> {
|
async createTablesFromSchema(tables: SQLiteDBTableSchema[]): Promise<void> {
|
||||||
const promises = [];
|
const promises = tables.map(table => this.createTableFromSchema(table));
|
||||||
tables.forEach((table) => {
|
|
||||||
promises.push(this.createTableFromSchema(table));
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
@ -432,7 +429,7 @@ export class SQLiteDB {
|
||||||
async execute(sql: string, params?: SQLiteDBRecordValue[]): Promise<any> {
|
async execute(sql: string, params?: SQLiteDBRecordValue[]): Promise<any> {
|
||||||
await this.ready();
|
await this.ready();
|
||||||
|
|
||||||
return this.db.executeSql(sql, params);
|
return this.db!.executeSql(sql, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -447,7 +444,7 @@ export class SQLiteDB {
|
||||||
async executeBatch(sqlStatements: (string | string[] | any)[]): Promise<void> {
|
async executeBatch(sqlStatements: (string | string[] | any)[]): Promise<void> {
|
||||||
await this.ready();
|
await this.ready();
|
||||||
|
|
||||||
await this.db.sqlBatch(sqlStatements);
|
await this.db!.sqlBatch(sqlStatements);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -532,7 +529,7 @@ export class SQLiteDB {
|
||||||
* @return Promise resolved with the field's value.
|
* @return Promise resolved with the field's value.
|
||||||
*/
|
*/
|
||||||
async getFieldSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValue> {
|
async getFieldSql(sql: string, params?: SQLiteDBRecordValue[]): Promise<SQLiteDBRecordValue> {
|
||||||
const record = await this.getRecordSql(sql, params);
|
const record = await this.getRecordSql<Record<string, SQLiteDBRecordValue>>(sql, params);
|
||||||
if (!record) {
|
if (!record) {
|
||||||
throw new CoreError('No record found.');
|
throw new CoreError('No record found.');
|
||||||
}
|
}
|
||||||
|
@ -552,7 +549,7 @@ export class SQLiteDB {
|
||||||
getInOrEqual(
|
getInOrEqual(
|
||||||
items: SQLiteDBRecordValue | SQLiteDBRecordValue[],
|
items: SQLiteDBRecordValue | SQLiteDBRecordValue[],
|
||||||
equal: boolean = true,
|
equal: boolean = true,
|
||||||
onEmptyItems?: SQLiteDBRecordValue,
|
onEmptyItems?: SQLiteDBRecordValue | null,
|
||||||
): SQLiteDBQueryParams {
|
): SQLiteDBQueryParams {
|
||||||
let sql = '';
|
let sql = '';
|
||||||
let params: SQLiteDBRecordValue[];
|
let params: SQLiteDBRecordValue[];
|
||||||
|
@ -564,7 +561,7 @@ export class SQLiteDB {
|
||||||
|
|
||||||
// Handle onEmptyItems on empty array of items.
|
// Handle onEmptyItems on empty array of items.
|
||||||
if (Array.isArray(items) && !items.length) {
|
if (Array.isArray(items) && !items.length) {
|
||||||
if (onEmptyItems === null) { // Special case, NULL value.
|
if (onEmptyItems === null || typeof onEmptyItems === 'undefined') { // Special case, NULL value.
|
||||||
sql = equal ? ' IS NULL' : ' IS NOT NULL';
|
sql = equal ? ' IS NULL' : ' IS NOT NULL';
|
||||||
|
|
||||||
return { sql, params: [] };
|
return { sql, params: [] };
|
||||||
|
@ -758,7 +755,7 @@ export class SQLiteDB {
|
||||||
|
|
||||||
const result = await this.execute(sql, params);
|
const result = await this.execute(sql, params);
|
||||||
// Retrieve the records.
|
// Retrieve the records.
|
||||||
const records = [];
|
const records: T[] = [];
|
||||||
for (let i = 0; i < result.rows.length; i++) {
|
for (let i = 0; i < result.rows.length; i++) {
|
||||||
records.push(result.rows.item(i));
|
records.push(result.rows.item(i));
|
||||||
}
|
}
|
||||||
|
@ -868,7 +865,7 @@ export class SQLiteDB {
|
||||||
* @param limitNum How many results to return.
|
* @param limitNum How many results to return.
|
||||||
* @return Normalised limit params in array: [limitFrom, limitNum].
|
* @return Normalised limit params in array: [limitFrom, limitNum].
|
||||||
*/
|
*/
|
||||||
normaliseLimitFromNum(limitFrom: number, limitNum: number): number[] {
|
normaliseLimitFromNum(limitFrom?: number, limitNum?: number): number[] {
|
||||||
// We explicilty treat these cases as 0.
|
// We explicilty treat these cases as 0.
|
||||||
if (!limitFrom || limitFrom === -1) {
|
if (!limitFrom || limitFrom === -1) {
|
||||||
limitFrom = 0;
|
limitFrom = 0;
|
||||||
|
@ -893,7 +890,7 @@ export class SQLiteDB {
|
||||||
async open(): Promise<void> {
|
async open(): Promise<void> {
|
||||||
await this.ready();
|
await this.ready();
|
||||||
|
|
||||||
await this.db.open();
|
await this.db!.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -994,11 +991,7 @@ export class SQLiteDB {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sets = [];
|
const sets = Object.keys(data).map(key => `${key} = ?`);
|
||||||
for (const key in data) {
|
|
||||||
sets.push(`${key} = ?`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let sql = `UPDATE ${table} SET ${sets.join(', ')}`;
|
let sql = `UPDATE ${table} SET ${sets.join(', ')}`;
|
||||||
if (where) {
|
if (where) {
|
||||||
sql += ` WHERE ${where}`;
|
sql += ` WHERE ${where}`;
|
||||||
|
@ -1029,8 +1022,8 @@ export class SQLiteDB {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const where = [];
|
const where: string[] = [];
|
||||||
const params = [];
|
const params: SQLiteDBRecordValue[] = [];
|
||||||
|
|
||||||
for (const key in conditions) {
|
for (const key in conditions) {
|
||||||
const value = conditions[key];
|
const value = conditions[key];
|
||||||
|
@ -1064,7 +1057,7 @@ export class SQLiteDB {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = [];
|
const params: SQLiteDBRecordValue[] = [];
|
||||||
let sql = '';
|
let sql = '';
|
||||||
|
|
||||||
values.forEach((value) => {
|
values.forEach((value) => {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import { SQLiteDB } from '@classes/sqlitedb';
|
||||||
* Class to mock the interaction with the SQLite database.
|
* Class to mock the interaction with the SQLite database.
|
||||||
*/
|
*/
|
||||||
export class SQLiteDBMock extends SQLiteDB {
|
export class SQLiteDBMock extends SQLiteDB {
|
||||||
promise: Promise<void>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and open the database.
|
* Create and open the database.
|
||||||
|
@ -46,9 +45,11 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
*
|
*
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
emptyDatabase(): Promise<any> {
|
async emptyDatabase(): Promise<any> {
|
||||||
|
await this.ready();
|
||||||
|
|
||||||
return new Promise((resolve, reject): void => {
|
return new Promise((resolve, reject): void => {
|
||||||
this.db.transaction((tx) => {
|
this.db!.transaction((tx) => {
|
||||||
// Query all tables from sqlite_master that we have created and can modify.
|
// Query all tables from sqlite_master that we have created and can modify.
|
||||||
const args = [];
|
const args = [];
|
||||||
const query = `SELECT * FROM sqlite_master
|
const query = `SELECT * FROM sqlite_master
|
||||||
|
@ -63,7 +64,7 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop all the tables.
|
// Drop all the tables.
|
||||||
const promises = [];
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < result.rows.length; i++) {
|
for (let i = 0; i < result.rows.length; i++) {
|
||||||
promises.push(new Promise((resolve, reject): void => {
|
promises.push(new Promise((resolve, reject): void => {
|
||||||
|
@ -73,7 +74,7 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(promises).then(resolve, reject);
|
Promise.all(promises).then(resolve).catch(reject);
|
||||||
}, reject);
|
}, reject);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -88,14 +89,18 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
* @param params Query parameters.
|
* @param params Query parameters.
|
||||||
* @return Promise resolved with the result.
|
* @return Promise resolved with the result.
|
||||||
*/
|
*/
|
||||||
execute(sql: string, params?: any[]): Promise<any> {
|
async execute(sql: string, params?: any[]): Promise<any> {
|
||||||
|
await this.ready();
|
||||||
|
|
||||||
return new Promise((resolve, reject): void => {
|
return new Promise((resolve, reject): void => {
|
||||||
// With WebSQL, all queries must be run in a transaction.
|
// With WebSQL, all queries must be run in a transaction.
|
||||||
this.db.transaction((tx) => {
|
this.db!.transaction((tx) => {
|
||||||
tx.executeSql(sql, params, (tx, results) => {
|
tx.executeSql(sql, params, (tx, results) => {
|
||||||
resolve(results);
|
resolve(results);
|
||||||
}, (tx, error) => {
|
}, (tx, error) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.error(sql, params, error);
|
console.error(sql, params, error);
|
||||||
|
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -110,11 +115,13 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
* @param sqlStatements SQL statements to execute.
|
* @param sqlStatements SQL statements to execute.
|
||||||
* @return Promise resolved with the result.
|
* @return Promise resolved with the result.
|
||||||
*/
|
*/
|
||||||
executeBatch(sqlStatements: any[]): Promise<any> {
|
async executeBatch(sqlStatements: any[]): Promise<any> {
|
||||||
|
await this.ready();
|
||||||
|
|
||||||
return new Promise((resolve, reject): void => {
|
return new Promise((resolve, reject): void => {
|
||||||
// Create a transaction to execute the queries.
|
// Create a transaction to execute the queries.
|
||||||
this.db.transaction((tx) => {
|
this.db!.transaction((tx) => {
|
||||||
const promises = [];
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
// Execute all the queries. Each statement can be a string or an array.
|
// Execute all the queries. Each statement can be a string or an array.
|
||||||
sqlStatements.forEach((statement) => {
|
sqlStatements.forEach((statement) => {
|
||||||
|
@ -133,7 +140,9 @@ export class SQLiteDBMock extends SQLiteDB {
|
||||||
tx.executeSql(query, params, (tx, results) => {
|
tx.executeSql(query, params, (tx, results) => {
|
||||||
resolve(results);
|
resolve(results);
|
||||||
}, (tx, error) => {
|
}, (tx, error) => {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.error(query, params, error);
|
console.error(query, params, error);
|
||||||
|
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -45,13 +45,13 @@ export class CoreAppProvider {
|
||||||
|
|
||||||
protected db: SQLiteDB;
|
protected db: SQLiteDB;
|
||||||
protected logger: CoreLogger;
|
protected logger: CoreLogger;
|
||||||
protected ssoAuthenticationDeferred: PromiseDefer<void>;
|
protected ssoAuthenticationDeferred?: PromiseDefer<void>;
|
||||||
protected isKeyboardShown = false;
|
protected isKeyboardShown = false;
|
||||||
protected keyboardOpening = false;
|
protected keyboardOpening = false;
|
||||||
protected keyboardClosing = false;
|
protected keyboardClosing = false;
|
||||||
protected backActions: {callback: () => boolean; priority: number}[] = [];
|
protected backActions: {callback: () => boolean; priority: number}[] = [];
|
||||||
protected mainMenuId = 0;
|
protected mainMenuId = 0;
|
||||||
protected mainMenuOpen: number;
|
protected mainMenuOpen?: number;
|
||||||
protected forceOffline = false;
|
protected forceOffline = false;
|
||||||
|
|
||||||
// Variables for DB.
|
// Variables for DB.
|
||||||
|
@ -224,7 +224,7 @@ export class CoreAppProvider {
|
||||||
* @param storesConfig Config params to send the user to the right place.
|
* @param storesConfig Config params to send the user to the right place.
|
||||||
* @return Store URL.
|
* @return Store URL.
|
||||||
*/
|
*/
|
||||||
getAppStoreUrl(storesConfig: CoreStoreConfig): string {
|
getAppStoreUrl(storesConfig: CoreStoreConfig): string | null {
|
||||||
if (this.isMac() && storesConfig.mac) {
|
if (this.isMac() && storesConfig.mac) {
|
||||||
return 'itms-apps://itunes.apple.com/app/' + storesConfig.mac;
|
return 'itms-apps://itunes.apple.com/app/' + storesConfig.mac;
|
||||||
}
|
}
|
||||||
|
@ -332,6 +332,8 @@ export class CoreAppProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// @todo return require('os').platform().indexOf('linux') === 0;
|
// @todo return require('os').platform().indexOf('linux') === 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -349,6 +351,8 @@ export class CoreAppProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// @todo return require('os').platform().indexOf('darwin') === 0;
|
// @todo return require('os').platform().indexOf('darwin') === 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -439,6 +443,8 @@ export class CoreAppProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// @todo return require('os').platform().indexOf('win') === 0;
|
// @todo return require('os').platform().indexOf('win') === 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -521,7 +527,9 @@ export class CoreAppProvider {
|
||||||
* @return Promise resolved once SSO authentication finishes.
|
* @return Promise resolved once SSO authentication finishes.
|
||||||
*/
|
*/
|
||||||
async waitForSSOAuthentication(): Promise<void> {
|
async waitForSSOAuthentication(): Promise<void> {
|
||||||
await this.ssoAuthenticationDeferred && this.ssoAuthenticationDeferred.promise;
|
const promise = this.ssoAuthenticationDeferred?.promise;
|
||||||
|
|
||||||
|
await promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -530,7 +538,7 @@ export class CoreAppProvider {
|
||||||
* @param timeout Maximum time to wait, use null to wait forever.
|
* @param timeout Maximum time to wait, use null to wait forever.
|
||||||
*/
|
*/
|
||||||
async waitForResume(timeout: number | null = null): Promise<void> {
|
async waitForResume(timeout: number | null = null): Promise<void> {
|
||||||
let deferred = CoreUtils.instance.promiseDefer<void>();
|
let deferred: PromiseDefer<void> | null = CoreUtils.instance.promiseDefer<void>();
|
||||||
|
|
||||||
const stopWaiting = () => {
|
const stopWaiting = () => {
|
||||||
if (!deferred) {
|
if (!deferred) {
|
||||||
|
@ -556,13 +564,13 @@ export class CoreAppProvider {
|
||||||
* @return Object with siteid, state, params and timemodified.
|
* @return Object with siteid, state, params and timemodified.
|
||||||
*/
|
*/
|
||||||
getRedirect<Params extends Record<string, unknown> = Record<string, unknown>>(): CoreRedirectData<Params> {
|
getRedirect<Params extends Record<string, unknown> = Record<string, unknown>>(): CoreRedirectData<Params> {
|
||||||
if (localStorage && localStorage.getItem) {
|
if (localStorage?.getItem) {
|
||||||
try {
|
try {
|
||||||
const paramsJson = localStorage.getItem('CoreRedirectParams');
|
const paramsJson = localStorage.getItem('CoreRedirectParams');
|
||||||
const data: CoreRedirectData<Params> = {
|
const data: CoreRedirectData<Params> = {
|
||||||
siteId: localStorage.getItem('CoreRedirectSiteId'),
|
siteId: localStorage.getItem('CoreRedirectSiteId') || undefined,
|
||||||
page: localStorage.getItem('CoreRedirectState'),
|
page: localStorage.getItem('CoreRedirectState') || undefined,
|
||||||
timemodified: parseInt(localStorage.getItem('CoreRedirectTime'), 10),
|
timemodified: parseInt(localStorage.getItem('CoreRedirectTime') || '0', 10),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (paramsJson) {
|
if (paramsJson) {
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { CoreUtils, PromiseDefer } from '@services/utils/utils';
|
import { CoreUtils, PromiseDefer, OrderedPromiseData } from '@services/utils/utils';
|
||||||
import { CoreLogger } from '@singletons/logger';
|
import { CoreLogger } from '@singletons/logger';
|
||||||
import { makeSingleton } from '@singletons/core.singletons';
|
import { makeSingleton } from '@singletons/core.singletons';
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ export class CoreInitDelegate {
|
||||||
|
|
||||||
protected initProcesses: { [s: string]: CoreInitHandler } = {};
|
protected initProcesses: { [s: string]: CoreInitHandler } = {};
|
||||||
protected logger: CoreLogger;
|
protected logger: CoreLogger;
|
||||||
protected readiness: CoreInitReadinessPromiseDefer<void>;
|
protected readiness?: CoreInitReadinessPromiseDefer<void>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.logger = CoreLogger.getInstance('CoreInitDelegate');
|
this.logger = CoreLogger.getInstance('CoreInitDelegate');
|
||||||
|
@ -68,7 +68,7 @@ export class CoreInitDelegate {
|
||||||
* Reserved for core use, do not call directly.
|
* Reserved for core use, do not call directly.
|
||||||
*/
|
*/
|
||||||
executeInitProcesses(): void {
|
executeInitProcesses(): void {
|
||||||
let ordered = [];
|
const ordered: CoreInitHandler[] = [];
|
||||||
|
|
||||||
if (typeof this.readiness == 'undefined') {
|
if (typeof this.readiness == 'undefined') {
|
||||||
this.initReadiness();
|
this.initReadiness();
|
||||||
|
@ -78,15 +78,15 @@ export class CoreInitDelegate {
|
||||||
for (const name in this.initProcesses) {
|
for (const name in this.initProcesses) {
|
||||||
ordered.push(this.initProcesses[name]);
|
ordered.push(this.initProcesses[name]);
|
||||||
}
|
}
|
||||||
ordered.sort((a, b) => b.priority - a.priority);
|
ordered.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
||||||
|
|
||||||
ordered = ordered.map((data: CoreInitHandler) => ({
|
const orderedPromises: OrderedPromiseData[] = ordered.map((data: CoreInitHandler) => ({
|
||||||
function: this.prepareProcess.bind(this, data),
|
function: this.prepareProcess.bind(this, data),
|
||||||
blocking: !!data.blocking,
|
blocking: !!data.blocking,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Execute all the processes in order to solve dependencies.
|
// Execute all the processes in order to solve dependencies.
|
||||||
CoreUtils.instance.executeOrderedPromises(ordered).finally(this.readiness.resolve);
|
CoreUtils.instance.executeOrderedPromises(orderedPromises).finally(this.readiness!.resolve);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,7 +94,9 @@ export class CoreInitDelegate {
|
||||||
*/
|
*/
|
||||||
protected initReadiness(): void {
|
protected initReadiness(): void {
|
||||||
this.readiness = CoreUtils.instance.promiseDefer();
|
this.readiness = CoreUtils.instance.promiseDefer();
|
||||||
this.readiness.promise.then(() => this.readiness.resolved = true);
|
|
||||||
|
// eslint-disable-next-line promise/catch-or-return
|
||||||
|
this.readiness.promise.then(() => this.readiness!.resolved = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,7 +105,7 @@ export class CoreInitDelegate {
|
||||||
* @return Whether it's ready.
|
* @return Whether it's ready.
|
||||||
*/
|
*/
|
||||||
isReady(): boolean {
|
isReady(): boolean {
|
||||||
return this.readiness.resolved;
|
return this.readiness?.resolved || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,7 +135,7 @@ export class CoreInitDelegate {
|
||||||
this.initReadiness();
|
this.initReadiness();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.readiness.promise;
|
await this.readiness!.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,9 +30,9 @@ export class CoreLangProvider {
|
||||||
|
|
||||||
protected fallbackLanguage = 'en'; // Always use English as fallback language since it contains all strings.
|
protected fallbackLanguage = 'en'; // Always use English as fallback language since it contains all strings.
|
||||||
protected defaultLanguage = CoreConfigConstants.default_lang || 'en'; // Lang to use if device lang not valid or is forced.
|
protected defaultLanguage = CoreConfigConstants.default_lang || 'en'; // Lang to use if device lang not valid or is forced.
|
||||||
protected currentLanguage: string; // Save current language in a variable to speed up the get function.
|
protected currentLanguage?: string; // Save current language in a variable to speed up the get function.
|
||||||
protected customStrings: CoreLanguageObject = {}; // Strings defined using the admin tool.
|
protected customStrings: CoreLanguageObject = {}; // Strings defined using the admin tool.
|
||||||
protected customStringsRaw: string;
|
protected customStringsRaw?: string;
|
||||||
protected sitePluginsStrings: CoreLanguageObject = {}; // Strings defined by site plugins.
|
protected sitePluginsStrings: CoreLanguageObject = {}; // Strings defined by site plugins.
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -123,7 +123,7 @@ export class CoreLangProvider {
|
||||||
* @return Promise resolved when the change is finished.
|
* @return Promise resolved when the change is finished.
|
||||||
*/
|
*/
|
||||||
async changeCurrentLanguage(language: string): Promise<void> {
|
async changeCurrentLanguage(language: string): Promise<void> {
|
||||||
const promises = [];
|
const promises: Promise<unknown>[] = [];
|
||||||
|
|
||||||
// Change the language, resolving the promise when we receive the first value.
|
// Change the language, resolving the promise when we receive the first value.
|
||||||
promises.push(new Promise((resolve, reject) => {
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
@ -363,8 +363,8 @@ export class CoreLangProvider {
|
||||||
if (currentLangChanged) {
|
if (currentLangChanged) {
|
||||||
// Some lang strings have changed, emit an event to update the pipes.
|
// Some lang strings have changed, emit an event to update the pipes.
|
||||||
Translate.instance.onLangChange.emit({
|
Translate.instance.onLangChange.emit({
|
||||||
lang: this.currentLanguage,
|
lang: this.currentLanguage!,
|
||||||
translations: Translate.instance.translations[this.currentLanguage],
|
translations: Translate.instance.translations[this.currentLanguage!],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ export class CoreIframeUtilsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the warning and show the iframe
|
// Remove the warning and show the iframe
|
||||||
CoreDomUtils.instance.removeElement(element.parentElement, 'div.core-iframe-offline-warning');
|
CoreDomUtils.instance.removeElement(element.parentElement!, 'div.core-iframe-offline-warning');
|
||||||
element.classList.remove('core-iframe-offline-disabled');
|
element.classList.remove('core-iframe-offline-disabled');
|
||||||
|
|
||||||
if (isSubframe) {
|
if (isSubframe) {
|
||||||
|
@ -131,9 +131,9 @@ export class CoreIframeUtilsProvider {
|
||||||
* @param element Element to treat (iframe, embed, ...).
|
* @param element Element to treat (iframe, embed, ...).
|
||||||
* @return Window and Document.
|
* @return Window and Document.
|
||||||
*/
|
*/
|
||||||
getContentWindowAndDocument(element: CoreFrameElement): { window: Window; document: Document } {
|
getContentWindowAndDocument(element: CoreFrameElement): { window: Window | null; document: Document | null } {
|
||||||
let contentWindow: Window = 'contentWindow' in element ? element.contentWindow : undefined;
|
let contentWindow: Window | null = 'contentWindow' in element ? element.contentWindow : null;
|
||||||
let contentDocument: Document;
|
let contentDocument: Document | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
contentDocument = 'contentDocument' in element && element.contentDocument
|
contentDocument = 'contentDocument' in element && element.contentDocument
|
||||||
|
@ -209,7 +209,8 @@ export class CoreIframeUtilsProvider {
|
||||||
contentWindow.open = (url: string, name: string) => {
|
contentWindow.open = (url: string, name: string) => {
|
||||||
this.windowOpen(url, name, element, navCtrl);
|
this.windowOpen(url, name, element, navCtrl);
|
||||||
|
|
||||||
return null;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
return null as any;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,31 +234,35 @@ export class CoreIframeUtilsProvider {
|
||||||
* @param navCtrl NavController to use if a link can be opened in the app.
|
* @param navCtrl NavController to use if a link can be opened in the app.
|
||||||
*/
|
*/
|
||||||
treatFrame(element: CoreFrameElement, isSubframe?: boolean, navCtrl?: NavController): void {
|
treatFrame(element: CoreFrameElement, isSubframe?: boolean, navCtrl?: NavController): void {
|
||||||
if (element) {
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const treatElement = (sendResizeEvent: boolean = false) => {
|
||||||
this.checkOnlineFrameInOffline(element, isSubframe);
|
this.checkOnlineFrameInOffline(element, isSubframe);
|
||||||
|
|
||||||
let winAndDoc = this.getContentWindowAndDocument(element);
|
const { window, document } = this.getContentWindowAndDocument(element);
|
||||||
|
|
||||||
// Redefine window.open in this element and sub frames, it might have been loaded already.
|
// Redefine window.open in this element and sub frames, it might have been loaded already.
|
||||||
this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document, navCtrl);
|
if (window && document) {
|
||||||
// Treat links.
|
this.redefineWindowOpen(element, window, document, navCtrl);
|
||||||
this.treatFrameLinks(element, winAndDoc.document);
|
}
|
||||||
|
|
||||||
element.addEventListener('load', () => {
|
// Treat links.
|
||||||
this.checkOnlineFrameInOffline(element, isSubframe);
|
if (document) {
|
||||||
|
this.treatFrameLinks(element, document);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a resize events to the iframe so it calculates the right size if needed.
|
||||||
|
if (window && sendResizeEvent) {
|
||||||
|
setTimeout(() => window.dispatchEvent(new Event('resize')), 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
treatElement();
|
||||||
|
|
||||||
// Element loaded, redefine window.open and treat links again.
|
// Element loaded, redefine window.open and treat links again.
|
||||||
winAndDoc = this.getContentWindowAndDocument(element);
|
element.addEventListener('load', () => treatElement(true));
|
||||||
this.redefineWindowOpen(element, winAndDoc.window, winAndDoc.document, navCtrl);
|
|
||||||
this.treatFrameLinks(element, winAndDoc.document);
|
|
||||||
|
|
||||||
if (winAndDoc.window) {
|
|
||||||
// Send a resize events to the iframe so it calculates the right size if needed.
|
|
||||||
setTimeout(() => {
|
|
||||||
winAndDoc.window.dispatchEvent(new Event('resize'));
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -279,7 +284,7 @@ export class CoreIframeUtilsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the link being clicked.
|
// Find the link being clicked.
|
||||||
let el = <Element> event.target;
|
let el: Element | null = event.target as Element;
|
||||||
while (el && el.tagName !== 'A') {
|
while (el && el.tagName !== 'A') {
|
||||||
el = el.parentElement;
|
el = el.parentElement;
|
||||||
}
|
}
|
||||||
|
@ -386,19 +391,24 @@ export class CoreIframeUtilsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlParts = CoreUrl.parse(link.href);
|
const urlParts = CoreUrl.parse(link.href);
|
||||||
if (!link.href || (urlParts.protocol && urlParts.protocol == 'javascript')) {
|
if (!link.href || !urlParts || (urlParts.protocol && urlParts.protocol == 'javascript')) {
|
||||||
// Links with no URL and Javascript links are ignored.
|
// Links with no URL and Javascript links are ignored.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CoreUrlUtils.instance.isLocalFileUrlScheme(urlParts.protocol)) {
|
if (urlParts.protocol && !CoreUrlUtils.instance.isLocalFileUrlScheme(urlParts.protocol)) {
|
||||||
// Scheme suggests it's an external resource.
|
// Scheme suggests it's an external resource.
|
||||||
event && event.preventDefault();
|
event && event.preventDefault();
|
||||||
|
|
||||||
const frameSrc = element && ((<HTMLFrameElement> element).src || (<HTMLObjectElement> element).data);
|
const frameSrc = element && ((<HTMLFrameElement> element).src || (<HTMLObjectElement> element).data);
|
||||||
|
|
||||||
// If the frame is not local, check the target to identify how to treat the link.
|
// If the frame is not local, check the target to identify how to treat the link.
|
||||||
if (element && !CoreUrlUtils.instance.isLocalFileUrl(frameSrc) && (!link.target || link.target == '_self')) {
|
if (
|
||||||
|
element &&
|
||||||
|
frameSrc &&
|
||||||
|
!CoreUrlUtils.instance.isLocalFileUrl(frameSrc) &&
|
||||||
|
(!link.target || link.target == '_self')
|
||||||
|
) {
|
||||||
// Load the link inside the frame itself.
|
// Load the link inside the frame itself.
|
||||||
if (element.tagName.toLowerCase() == 'object') {
|
if (element.tagName.toLowerCase() == 'object') {
|
||||||
element.setAttribute('data', link.href);
|
element.setAttribute('data', link.href);
|
||||||
|
@ -455,8 +465,8 @@ export class CoreIframeUtilsProvider {
|
||||||
const linksPath = CoreTextUtils.instance.concatenatePaths(wwwPath, 'assets/js/iframe-treat-links.js');
|
const linksPath = CoreTextUtils.instance.concatenatePaths(wwwPath, 'assets/js/iframe-treat-links.js');
|
||||||
const recaptchaPath = CoreTextUtils.instance.concatenatePaths(wwwPath, 'assets/js/iframe-recaptcha.js');
|
const recaptchaPath = CoreTextUtils.instance.concatenatePaths(wwwPath, 'assets/js/iframe-recaptcha.js');
|
||||||
|
|
||||||
userScriptWindow.WKUserScript.addScript({ id: 'CoreIframeUtilsLinksScript', file: linksPath });
|
userScriptWindow.WKUserScript?.addScript({ id: 'CoreIframeUtilsLinksScript', file: linksPath });
|
||||||
userScriptWindow.WKUserScript.addScript({
|
userScriptWindow.WKUserScript?.addScript({
|
||||||
id: 'CoreIframeUtilsRecaptchaScript',
|
id: 'CoreIframeUtilsRecaptchaScript',
|
||||||
file: recaptchaPath,
|
file: recaptchaPath,
|
||||||
injectionTime: WKUserScriptInjectionTime.END,
|
injectionTime: WKUserScriptInjectionTime.END,
|
||||||
|
|
|
@ -116,7 +116,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
*/
|
*/
|
||||||
protected fillGroupMimeInfo(group: string): void {
|
protected fillGroupMimeInfo(group: string): void {
|
||||||
const mimetypes = {}; // Use an object to prevent duplicates.
|
const mimetypes = {}; // Use an object to prevent duplicates.
|
||||||
const extensions = []; // Extensions are unique.
|
const extensions: string[] = []; // Extensions are unique.
|
||||||
|
|
||||||
for (const extension in this.extToMime) {
|
for (const extension in this.extToMime) {
|
||||||
const data = this.extToMime[extension];
|
const data = this.extToMime[extension];
|
||||||
|
@ -140,13 +140,13 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param url URL of the file. It will be used if there's more than one possible extension.
|
* @param url URL of the file. It will be used if there's more than one possible extension.
|
||||||
* @return Extension.
|
* @return Extension.
|
||||||
*/
|
*/
|
||||||
getExtension(mimetype: string, url?: string): string {
|
getExtension(mimetype: string, url?: string): string | undefined {
|
||||||
mimetype = mimetype || '';
|
mimetype = mimetype || '';
|
||||||
mimetype = mimetype.split(';')[0]; // Remove codecs from the mimetype if any.
|
mimetype = mimetype.split(';')[0]; // Remove codecs from the mimetype if any.
|
||||||
|
|
||||||
if (mimetype == 'application/x-forcedownload' || mimetype == 'application/forcedownload') {
|
if (mimetype == 'application/x-forcedownload' || mimetype == 'application/forcedownload') {
|
||||||
// Couldn't get the right mimetype, try to guess it.
|
// Couldn't get the right mimetype, try to guess it.
|
||||||
return this.guessExtensionFromUrl(url);
|
return url && this.guessExtensionFromUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
const extensions = this.mimeToExt[mimetype];
|
const extensions = this.mimeToExt[mimetype];
|
||||||
|
@ -154,7 +154,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
if (extensions.length > 1 && url) {
|
if (extensions.length > 1 && url) {
|
||||||
// There's more than one possible extension. Check if the URL has extension.
|
// There's more than one possible extension. Check if the URL has extension.
|
||||||
const candidate = this.guessExtensionFromUrl(url);
|
const candidate = this.guessExtensionFromUrl(url);
|
||||||
if (extensions.indexOf(candidate) != -1) {
|
if (candidate && extensions.indexOf(candidate) != -1) {
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,19 +173,22 @@ export class CoreMimetypeUtilsProvider {
|
||||||
const filename = CoreUtils.instance.isFileEntry(file) ? (file as FileEntry).name : file.filename;
|
const filename = CoreUtils.instance.isFileEntry(file) ? (file as FileEntry).name : file.filename;
|
||||||
const extension = !CoreUtils.instance.isFileEntry(file) && file.mimetype
|
const extension = !CoreUtils.instance.isFileEntry(file) && file.mimetype
|
||||||
? this.getExtension(file.mimetype)
|
? this.getExtension(file.mimetype)
|
||||||
: this.getFileExtension(filename);
|
: (filename && this.getFileExtension(filename));
|
||||||
const mimeType = !CoreUtils.instance.isFileEntry(file) && file.mimetype ? file.mimetype : this.getMimeType(extension);
|
const mimeType = !CoreUtils.instance.isFileEntry(file) && file.mimetype
|
||||||
|
? file.mimetype
|
||||||
|
: (extension && this.getMimeType(extension));
|
||||||
|
|
||||||
// @todo linting: See if this can be removed
|
// @todo linting: See if this can be removed
|
||||||
(file as CoreWSExternalFile).mimetype = mimeType;
|
(file as CoreWSExternalFile).mimetype = mimeType;
|
||||||
|
|
||||||
if (this.canBeEmbedded(extension)) {
|
if (extension && this.canBeEmbedded(extension)) {
|
||||||
const embedType = this.getExtensionType(extension);
|
const embedType = this.getExtensionType(extension);
|
||||||
|
|
||||||
// @todo linting: See if this can be removed
|
// @todo linting: See if this can be removed
|
||||||
(file as { embedType: string }).embedType = embedType;
|
(file as { embedType?: string }).embedType = embedType;
|
||||||
|
|
||||||
path = CoreFile.instance.convertFileSrc(path ?? (CoreUtils.instance.isFileEntry(file) ? file.toURL() : file.fileurl));
|
path = path ?? (CoreUtils.instance.isFileEntry(file) ? file.toURL() : file.fileurl);
|
||||||
|
path = path && CoreFile.instance.convertFileSrc(path);
|
||||||
|
|
||||||
switch (embedType) {
|
switch (embedType) {
|
||||||
case 'image':
|
case 'image':
|
||||||
|
@ -223,7 +226,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param extension Extension.
|
* @param extension Extension.
|
||||||
* @return Icon. Undefined if not found.
|
* @return Icon. Undefined if not found.
|
||||||
*/
|
*/
|
||||||
getExtensionIconName(extension: string): string {
|
getExtensionIconName(extension: string): string | undefined {
|
||||||
if (this.extToMime[extension]) {
|
if (this.extToMime[extension]) {
|
||||||
if (this.extToMime[extension].icon) {
|
if (this.extToMime[extension].icon) {
|
||||||
return this.extToMime[extension].icon;
|
return this.extToMime[extension].icon;
|
||||||
|
@ -242,7 +245,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param extension Extension.
|
* @param extension Extension.
|
||||||
* @return Type of the extension.
|
* @return Type of the extension.
|
||||||
*/
|
*/
|
||||||
getExtensionType(extension: string): string {
|
getExtensionType(extension: string): string | undefined {
|
||||||
extension = this.cleanExtension(extension);
|
extension = this.cleanExtension(extension);
|
||||||
|
|
||||||
if (this.extToMime[extension] && this.extToMime[extension].string) {
|
if (this.extToMime[extension] && this.extToMime[extension].string) {
|
||||||
|
@ -270,8 +273,8 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @return The path to a file icon.
|
* @return The path to a file icon.
|
||||||
*/
|
*/
|
||||||
getFileIcon(filename: string): string {
|
getFileIcon(filename: string): string {
|
||||||
const ext = this.getFileExtension(filename);
|
const extension = this.getFileExtension(filename);
|
||||||
const icon = this.getExtensionIconName(ext) || 'unknown';
|
const icon = (extension && this.getExtensionIconName(extension)) || 'unknown';
|
||||||
|
|
||||||
return this.getFileIconForType(icon);
|
return this.getFileIconForType(icon);
|
||||||
}
|
}
|
||||||
|
@ -302,14 +305,14 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param fileUrl The file URL.
|
* @param fileUrl The file URL.
|
||||||
* @return The lowercased extension without the dot, or undefined.
|
* @return The lowercased extension without the dot, or undefined.
|
||||||
*/
|
*/
|
||||||
guessExtensionFromUrl(fileUrl: string): string {
|
guessExtensionFromUrl(fileUrl: string): string | undefined {
|
||||||
const split = fileUrl.split('.');
|
const split = fileUrl.split('.');
|
||||||
let candidate;
|
let candidate;
|
||||||
let extension;
|
let extension;
|
||||||
let position;
|
let position;
|
||||||
|
|
||||||
if (split.length > 1) {
|
if (split.length > 1) {
|
||||||
candidate = split.pop().toLowerCase();
|
candidate = split.pop()!.toLowerCase();
|
||||||
// Remove params if any.
|
// Remove params if any.
|
||||||
position = candidate.indexOf('?');
|
position = candidate.indexOf('?');
|
||||||
if (position > -1) {
|
if (position > -1) {
|
||||||
|
@ -338,7 +341,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param filename The file name.
|
* @param filename The file name.
|
||||||
* @return The lowercased extension, or undefined.
|
* @return The lowercased extension, or undefined.
|
||||||
*/
|
*/
|
||||||
getFileExtension(filename: string): string {
|
getFileExtension(filename: string): string | undefined {
|
||||||
const dot = filename.lastIndexOf('.');
|
const dot = filename.lastIndexOf('.');
|
||||||
let ext;
|
let ext;
|
||||||
|
|
||||||
|
@ -382,7 +385,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param extension Extension.
|
* @param extension Extension.
|
||||||
* @return Mimetype.
|
* @return Mimetype.
|
||||||
*/
|
*/
|
||||||
getMimeType(extension: string): string {
|
getMimeType(extension: string): string | undefined {
|
||||||
extension = this.cleanExtension(extension);
|
extension = this.cleanExtension(extension);
|
||||||
|
|
||||||
if (this.extToMime[extension] && this.extToMime[extension].type) {
|
if (this.extToMime[extension] && this.extToMime[extension].type) {
|
||||||
|
@ -400,9 +403,9 @@ export class CoreMimetypeUtilsProvider {
|
||||||
*/
|
*/
|
||||||
getMimetypeDescription(obj: FileEntry | { filename: string; mimetype: string } | string, capitalise?: boolean): string {
|
getMimetypeDescription(obj: FileEntry | { filename: string; mimetype: string } | string, capitalise?: boolean): string {
|
||||||
const langPrefix = 'assets.mimetypes.';
|
const langPrefix = 'assets.mimetypes.';
|
||||||
let filename = '';
|
let filename: string | undefined = '';
|
||||||
let mimetype = '';
|
let mimetype: string | undefined = '';
|
||||||
let extension = '';
|
let extension: string | undefined = '';
|
||||||
|
|
||||||
if (typeof obj == 'object' && CoreUtils.instance.isFileEntry(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.
|
// It's a FileEntry. Don't use the file function because it's asynchronous and the type isn't reliable.
|
||||||
|
@ -419,7 +422,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
|
|
||||||
if (!mimetype) {
|
if (!mimetype) {
|
||||||
// Try to calculate the mimetype using the extension.
|
// Try to calculate the mimetype using the extension.
|
||||||
mimetype = this.getMimeType(extension);
|
mimetype = extension && this.getMimeType(extension);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,7 +481,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param mimetype Mimetype.
|
* @param mimetype Mimetype.
|
||||||
* @return Type of the mimetype.
|
* @return Type of the mimetype.
|
||||||
*/
|
*/
|
||||||
getMimetypeType(mimetype: string): string {
|
getMimetypeType(mimetype: string): string | undefined {
|
||||||
mimetype = mimetype.split(';')[0]; // Remove codecs from the mimetype if any.
|
mimetype = mimetype.split(';')[0]; // Remove codecs from the mimetype if any.
|
||||||
|
|
||||||
const extensions = this.mimeToExt[mimetype];
|
const extensions = this.mimeToExt[mimetype];
|
||||||
|
@ -542,9 +545,9 @@ export class CoreMimetypeUtilsProvider {
|
||||||
isExtensionInGroup(extension: string, groups: string[]): boolean {
|
isExtensionInGroup(extension: string, groups: string[]): boolean {
|
||||||
extension = this.cleanExtension(extension);
|
extension = this.cleanExtension(extension);
|
||||||
|
|
||||||
if (groups && groups.length && this.extToMime[extension] && this.extToMime[extension].groups) {
|
if (groups?.length && this.extToMime[extension]?.groups) {
|
||||||
for (let i = 0; i < this.extToMime[extension].groups.length; i++) {
|
for (let i = 0; i < this.extToMime[extension].groups!.length; i++) {
|
||||||
const group = this.extToMime[extension].groups[i];
|
const group = this.extToMime[extension].groups![i];
|
||||||
if (groups.indexOf(group) != -1) {
|
if (groups.indexOf(group) != -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,11 +175,11 @@ export class CoreTextUtilsProvider {
|
||||||
// Filter invalid messages, and convert them to messages in case they're errors.
|
// Filter invalid messages, and convert them to messages in case they're errors.
|
||||||
const messages: string[] = [];
|
const messages: string[] = [];
|
||||||
|
|
||||||
paragraphs.forEach((paragraph) => {
|
paragraphs.forEach(paragraph => {
|
||||||
// If it's an error, get its message.
|
// If it's an error, get its message.
|
||||||
const message = this.getErrorMessageFromError(paragraph);
|
const message = this.getErrorMessageFromError(paragraph);
|
||||||
|
|
||||||
if (paragraph) {
|
if (paragraph && message) {
|
||||||
messages.push(message);
|
messages.push(message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -248,8 +248,7 @@ export class CoreTextUtilsProvider {
|
||||||
// First, we use a regexpr.
|
// First, we use a regexpr.
|
||||||
text = text.replace(/(<([^>]+)>)/ig, '');
|
text = text.replace(/(<([^>]+)>)/ig, '');
|
||||||
// Then, we rely on the browser. We need to wrap the text to be sure is HTML.
|
// Then, we rely on the browser. We need to wrap the text to be sure is HTML.
|
||||||
const element = this.convertToElement(text);
|
text = this.convertToElement(text).textContent!;
|
||||||
text = element.textContent;
|
|
||||||
// Recover or remove new lines.
|
// Recover or remove new lines.
|
||||||
text = this.replaceNewLines(text, singleLine ? ' ' : '<br>');
|
text = this.replaceNewLines(text, singleLine ? ' ' : '<br>');
|
||||||
|
|
||||||
|
@ -326,7 +325,7 @@ export class CoreTextUtilsProvider {
|
||||||
text = text.replace(/_/gi, ' ');
|
text = text.replace(/_/gi, ' ');
|
||||||
|
|
||||||
// This RegEx will detect any word change including Unicode chars. Some languages without spaces won't be counted fine.
|
// This RegEx will detect any word change including Unicode chars. Some languages without spaces won't be counted fine.
|
||||||
return text.match(/\S+/gi).length;
|
return text.match(/\S+/gi)?.length || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -359,8 +358,7 @@ export class CoreTextUtilsProvider {
|
||||||
*/
|
*/
|
||||||
decodeHTMLEntities(text: string): string {
|
decodeHTMLEntities(text: string): string {
|
||||||
if (text) {
|
if (text) {
|
||||||
const element = this.convertToElement(text);
|
text = this.convertToElement(text).textContent!;
|
||||||
text = element.textContent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
|
@ -512,7 +510,7 @@ export class CoreTextUtilsProvider {
|
||||||
if (clean) {
|
if (clean) {
|
||||||
formatted = this.cleanTags(formatted, singleLine);
|
formatted = this.cleanTags(formatted, singleLine);
|
||||||
}
|
}
|
||||||
if (shortenLength > 0) {
|
if (shortenLength && shortenLength > 0) {
|
||||||
formatted = this.shortenText(formatted, shortenLength);
|
formatted = this.shortenText(formatted, shortenLength);
|
||||||
}
|
}
|
||||||
if (highlight) {
|
if (highlight) {
|
||||||
|
@ -529,10 +527,11 @@ export class CoreTextUtilsProvider {
|
||||||
* @param error Error object.
|
* @param error Error object.
|
||||||
* @return Error message, undefined if not found.
|
* @return Error message, undefined if not found.
|
||||||
*/
|
*/
|
||||||
getErrorMessageFromError(error: string | CoreError | CoreTextErrorObject): string {
|
getErrorMessageFromError(error?: string | CoreError | CoreTextErrorObject): string | undefined {
|
||||||
if (typeof error == 'string') {
|
if (typeof error == 'string') {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error instanceof CoreError) {
|
if (error instanceof CoreError) {
|
||||||
return error.message;
|
return error.message;
|
||||||
}
|
}
|
||||||
|
@ -546,12 +545,12 @@ export class CoreTextUtilsProvider {
|
||||||
* @param files Files to extract the URL from. They need to have the URL in a 'url' or 'fileurl' attribute.
|
* @param files Files to extract the URL from. They need to have the URL in a 'url' or 'fileurl' attribute.
|
||||||
* @return Pluginfile URL, undefined if no files found.
|
* @return Pluginfile URL, undefined if no files found.
|
||||||
*/
|
*/
|
||||||
getTextPluginfileUrl(files: CoreWSExternalFile[]): string {
|
getTextPluginfileUrl(files: CoreWSExternalFile[]): string | undefined {
|
||||||
if (files && files.length) {
|
if (files?.length) {
|
||||||
const url = files[0].fileurl;
|
const url = files[0].fileurl;
|
||||||
|
|
||||||
// Remove text after last slash (encoded or not).
|
// Remove text after last slash (encoded or not).
|
||||||
return url.substr(0, Math.max(url.lastIndexOf('/'), url.lastIndexOf('%2F')));
|
return url?.substr(0, Math.max(url.lastIndexOf('/'), url.lastIndexOf('%2F')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -876,7 +875,7 @@ export class CoreTextUtilsProvider {
|
||||||
// Current lang not found. Try to find the first language.
|
// Current lang not found. Try to find the first language.
|
||||||
const matches = text.match(anyLangRegEx);
|
const matches = text.match(anyLangRegEx);
|
||||||
if (matches && matches[0]) {
|
if (matches && matches[0]) {
|
||||||
language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)[1];
|
language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)![1];
|
||||||
currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)</(?:lang|span)>', 'g');
|
currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)</(?:lang|span)>', 'g');
|
||||||
} else {
|
} else {
|
||||||
// No multi-lang tag found, stop.
|
// No multi-lang tag found, stop.
|
||||||
|
|
|
@ -42,9 +42,9 @@ export class CoreUtilsProvider {
|
||||||
protected readonly DONT_CLONE = ['[object FileEntry]', '[object DirectoryEntry]', '[object DOMFileSystem]'];
|
protected readonly DONT_CLONE = ['[object FileEntry]', '[object DirectoryEntry]', '[object DOMFileSystem]'];
|
||||||
|
|
||||||
protected logger: CoreLogger;
|
protected logger: CoreLogger;
|
||||||
protected iabInstance: InAppBrowserObject;
|
protected iabInstance?: InAppBrowserObject;
|
||||||
protected uniqueIds: {[name: string]: number} = {};
|
protected uniqueIds: {[name: string]: number} = {};
|
||||||
protected qrScanData: {deferred: PromiseDefer<string>; observable: Subscription};
|
protected qrScanData?: {deferred: PromiseDefer<string>; observable: Subscription};
|
||||||
|
|
||||||
constructor(protected zone: NgZone) {
|
constructor(protected zone: NgZone) {
|
||||||
this.logger = CoreLogger.getInstance('CoreUtilsProvider');
|
this.logger = CoreLogger.getInstance('CoreUtilsProvider');
|
||||||
|
@ -61,24 +61,16 @@ export class CoreUtilsProvider {
|
||||||
* @return New error message.
|
* @return New error message.
|
||||||
*/
|
*/
|
||||||
addDataNotDownloadedError(error: Error | string, defaultError?: string): string {
|
addDataNotDownloadedError(error: Error | string, defaultError?: string): string {
|
||||||
let errorMessage = error;
|
const errorMessage = CoreTextUtils.instance.getErrorMessageFromError(error) || defaultError || '';
|
||||||
|
|
||||||
if (error && typeof error != 'string') {
|
|
||||||
errorMessage = CoreTextUtils.instance.getErrorMessageFromError(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof errorMessage != 'string') {
|
|
||||||
errorMessage = defaultError || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.isWebServiceError(error)) {
|
|
||||||
// Local error. Add an extra warning.
|
|
||||||
errorMessage += '<br><br>' + Translate.instance.instant('core.errorsomedatanotdownloaded');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (this.isWebServiceError(error)) {
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Local error. Add an extra warning.
|
||||||
|
return errorMessage + '<br><br>' + Translate.instance.instant('core.errorsomedatanotdownloaded');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to Promise.all, but if a promise fails this function's promise won't be rejected until ALL promises have finished.
|
* Similar to Promise.all, but if a promise fails this function's promise won't be rejected until ALL promises have finished.
|
||||||
*
|
*
|
||||||
|
@ -116,12 +108,16 @@ export class CoreUtilsProvider {
|
||||||
* @param result Object where to put the properties. If not defined, a new object will be created.
|
* @param result Object where to put the properties. If not defined, a new object will be created.
|
||||||
* @return The object.
|
* @return The object.
|
||||||
*/
|
*/
|
||||||
arrayToObject(array: unknown[], propertyName?: string, result?: unknown): unknown {
|
arrayToObject<T extends Record<string, unknown> | string>(
|
||||||
result = result || {};
|
array: T[],
|
||||||
array.forEach((entry) => {
|
propertyName?: string,
|
||||||
|
result: Record<string, T> = {},
|
||||||
|
): Record<string, T> {
|
||||||
|
for (const entry of array) {
|
||||||
const key = propertyName ? entry[propertyName] : entry;
|
const key = propertyName ? entry[propertyName] : entry;
|
||||||
|
|
||||||
result[key] = entry;
|
result[key] = entry;
|
||||||
});
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +140,7 @@ export class CoreUtilsProvider {
|
||||||
maxLevels: number = 0,
|
maxLevels: number = 0,
|
||||||
level: number = 0,
|
level: number = 0,
|
||||||
undefinedIsNull: boolean = true,
|
undefinedIsNull: boolean = true,
|
||||||
): boolean {
|
): boolean | undefined {
|
||||||
if (typeof itemA == 'function' || typeof itemB == 'function') {
|
if (typeof itemA == 'function' || typeof itemB == 'function') {
|
||||||
return true; // Don't compare functions.
|
return true; // Don't compare functions.
|
||||||
} else if (typeof itemA == 'object' && typeof itemB == 'object') {
|
} else if (typeof itemA == 'object' && typeof itemB == 'object') {
|
||||||
|
@ -266,9 +262,9 @@ export class CoreUtilsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
return newArray;
|
return newArray;
|
||||||
} else if (typeof source == 'object' && source !== null) {
|
} else if (this.isObject(source)) {
|
||||||
// Check if the object shouldn't be copied.
|
// Check if the object shouldn't be copied.
|
||||||
if (source && source.toString && this.DONT_CLONE.indexOf(source.toString()) != -1) {
|
if (source.toString && this.DONT_CLONE.indexOf(source.toString()) != -1) {
|
||||||
// Object shouldn't be copied, return it as it is.
|
// Object shouldn't be copied, return it as it is.
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
@ -365,7 +361,7 @@ export class CoreUtilsProvider {
|
||||||
* @return Promise resolved when all promises are resolved.
|
* @return Promise resolved when all promises are resolved.
|
||||||
*/
|
*/
|
||||||
executeOrderedPromises(orderedPromisesData: OrderedPromiseData[]): Promise<void> {
|
executeOrderedPromises(orderedPromisesData: OrderedPromiseData[]): Promise<void> {
|
||||||
const promises = [];
|
const promises: Promise<void>[] = [];
|
||||||
let dependency = Promise.resolve();
|
let dependency = Promise.resolve();
|
||||||
|
|
||||||
// Execute all the processes in order.
|
// Execute all the processes in order.
|
||||||
|
@ -465,8 +461,8 @@ export class CoreUtilsProvider {
|
||||||
checkAll?: boolean,
|
checkAll?: boolean,
|
||||||
...args: P
|
...args: P
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
const promises = [];
|
const promises: Promise<false | number>[] = [];
|
||||||
const enabledSites = [];
|
const enabledSites: string[] = [];
|
||||||
|
|
||||||
for (const i in siteIds) {
|
for (const i in siteIds) {
|
||||||
const siteId = siteIds[i];
|
const siteId = siteIds[i];
|
||||||
|
@ -626,7 +622,7 @@ export class CoreUtilsProvider {
|
||||||
// Get the keys of the countries.
|
// Get the keys of the countries.
|
||||||
return this.getCountryList().then((countries) => {
|
return this.getCountryList().then((countries) => {
|
||||||
// Sort translations.
|
// Sort translations.
|
||||||
const sortedCountries = [];
|
const sortedCountries: { code: string; name: string }[] = [];
|
||||||
|
|
||||||
Object.keys(countries).sort((a, b) => countries[a].localeCompare(countries[b])).forEach((key) => {
|
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] });
|
||||||
|
@ -669,7 +665,7 @@ export class CoreUtilsProvider {
|
||||||
const table = await CoreLang.instance.getTranslationTable(lang);
|
const table = await CoreLang.instance.getTranslationTable(lang);
|
||||||
|
|
||||||
// Gather all the keys for countries,
|
// Gather all the keys for countries,
|
||||||
const keys = [];
|
const keys: string[] = [];
|
||||||
|
|
||||||
for (const name in table) {
|
for (const name in table) {
|
||||||
if (name.indexOf('assets.countries.') === 0) {
|
if (name.indexOf('assets.countries.') === 0) {
|
||||||
|
@ -696,7 +692,7 @@ export class CoreUtilsProvider {
|
||||||
getMimeTypeFromUrl(url: string): Promise<string> {
|
getMimeTypeFromUrl(url: string): Promise<string> {
|
||||||
// First check if it can be guessed from the URL.
|
// First check if it can be guessed from the URL.
|
||||||
const extension = CoreMimetypeUtils.instance.guessExtensionFromUrl(url);
|
const extension = CoreMimetypeUtils.instance.guessExtensionFromUrl(url);
|
||||||
const mimetype = CoreMimetypeUtils.instance.getMimeType(extension);
|
const mimetype = extension && CoreMimetypeUtils.instance.getMimeType(extension);
|
||||||
|
|
||||||
if (mimetype) {
|
if (mimetype) {
|
||||||
return Promise.resolve(mimetype);
|
return Promise.resolve(mimetype);
|
||||||
|
@ -730,6 +726,16 @@ export class CoreUtilsProvider {
|
||||||
return 'isFile' in file;
|
return 'isFile' in file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a value is an object.
|
||||||
|
*
|
||||||
|
* @param object Variable.
|
||||||
|
* @return Type guard indicating if this is an object.
|
||||||
|
*/
|
||||||
|
isObject(object: unknown): object is Record<string, unknown> {
|
||||||
|
return typeof object === 'object' && object !== null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a list of files, check if there are repeated names.
|
* Given a list of files, check if there are repeated names.
|
||||||
*
|
*
|
||||||
|
@ -741,12 +747,12 @@ export class CoreUtilsProvider {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const names = [];
|
const names: string[] = [];
|
||||||
|
|
||||||
// Check if there are 2 files with the same name.
|
// Check if there are 2 files with the same name.
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const file = files[i];
|
const file = files[i];
|
||||||
const name = this.isFileEntry(file) ? file.name : file.filename;
|
const name = (this.isFileEntry(file) ? file.name : file.filename) || '';
|
||||||
|
|
||||||
if (names.indexOf(name) > -1) {
|
if (names.indexOf(name) > -1) {
|
||||||
return Translate.instance.instant('core.filenameexist', { $a: name });
|
return Translate.instance.instant('core.filenameexist', { $a: name });
|
||||||
|
@ -885,7 +891,7 @@ export class CoreUtilsProvider {
|
||||||
path = CoreFile.instance.unconvertFileSrc(path);
|
path = CoreFile.instance.unconvertFileSrc(path);
|
||||||
|
|
||||||
const extension = CoreMimetypeUtils.instance.getFileExtension(path);
|
const extension = CoreMimetypeUtils.instance.getFileExtension(path);
|
||||||
const mimetype = CoreMimetypeUtils.instance.getMimeType(extension);
|
const mimetype = extension && CoreMimetypeUtils.instance.getMimeType(extension);
|
||||||
|
|
||||||
if (mimetype == 'text/html' && CoreApp.instance.isAndroid()) {
|
if (mimetype == 'text/html' && CoreApp.instance.isAndroid()) {
|
||||||
// Open HTML local files in InAppBrowser, in system browser some embedded files aren't loaded.
|
// Open HTML local files in InAppBrowser, in system browser some embedded files aren't loaded.
|
||||||
|
@ -902,7 +908,7 @@ export class CoreUtilsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await FileOpener.instance.open(path, mimetype);
|
await FileOpener.instance.open(path, mimetype || '');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Error opening file ' + path + ' with mimetype ' + mimetype);
|
this.logger.error('Error opening file ' + path + ' with mimetype ' + mimetype);
|
||||||
this.logger.error('Error: ', JSON.stringify(error));
|
this.logger.error('Error: ', JSON.stringify(error));
|
||||||
|
@ -924,7 +930,7 @@ export class CoreUtilsProvider {
|
||||||
* @param options Override default options passed to InAppBrowser.
|
* @param options Override default options passed to InAppBrowser.
|
||||||
* @return The opened window.
|
* @return The opened window.
|
||||||
*/
|
*/
|
||||||
openInApp(url: string, options?: InAppBrowserOptions): InAppBrowserObject {
|
openInApp(url: string, options?: InAppBrowserOptions): InAppBrowserObject | undefined {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -950,7 +956,7 @@ export class CoreUtilsProvider {
|
||||||
|
|
||||||
if (CoreApp.instance.isDesktop() || CoreApp.instance.isMobile()) {
|
if (CoreApp.instance.isDesktop() || CoreApp.instance.isMobile()) {
|
||||||
let loadStopSubscription;
|
let loadStopSubscription;
|
||||||
const loadStartUrls = [];
|
const loadStartUrls: string[] = [];
|
||||||
|
|
||||||
// Trigger global events when a url is loaded or the window is closed. This is to make it work like in Ionic 1.
|
// Trigger global events when a url is loaded or the window is closed. This is to make it work like in Ionic 1.
|
||||||
const loadStartSubscription = this.iabInstance.on('loadstart').subscribe((event) => {
|
const loadStartSubscription = this.iabInstance.on('loadstart').subscribe((event) => {
|
||||||
|
@ -1076,10 +1082,10 @@ export class CoreUtilsProvider {
|
||||||
if (typeof value == 'undefined' || value == null) {
|
if (typeof value == 'undefined' || value == null) {
|
||||||
// Filter undefined and null values.
|
// Filter undefined and null values.
|
||||||
return;
|
return;
|
||||||
} else if (typeof value == 'object') {
|
} else if (this.isObject(value)) {
|
||||||
// It's an object, return at least an entry for each property.
|
// It's an object, return at least an entry for each property.
|
||||||
const keys = Object.keys(value);
|
const keys = Object.keys(value);
|
||||||
let entries = [];
|
let entries: unknown[] = [];
|
||||||
|
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
const newElKey = elKey ? elKey + '[' + key + ']' : key;
|
const newElKey = elKey ? elKey + '[' + key + ']' : key;
|
||||||
|
@ -1110,9 +1116,9 @@ export class CoreUtilsProvider {
|
||||||
if (sortByKey || sortByValue) {
|
if (sortByKey || sortByValue) {
|
||||||
return entries.sort((a, b) => {
|
return entries.sort((a, b) => {
|
||||||
if (sortByKey) {
|
if (sortByKey) {
|
||||||
return a[keyName] >= b[keyName] ? 1 : -1;
|
return (a[keyName] as number) >= (b[keyName] as number) ? 1 : -1;
|
||||||
} else {
|
} else {
|
||||||
return a[valueName] >= b[valueName] ? 1 : -1;
|
return (a[valueName] as number) >= (b[valueName] as number) ? 1 : -1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1135,7 +1141,7 @@ export class CoreUtilsProvider {
|
||||||
keyName: string,
|
keyName: string,
|
||||||
valueName: string,
|
valueName: string,
|
||||||
keyPrefix?: string,
|
keyPrefix?: string,
|
||||||
): {[name: string]: unknown} {
|
): {[name: string]: unknown} | undefined {
|
||||||
if (!objects) {
|
if (!objects) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1206,13 +1212,13 @@ export class CoreUtilsProvider {
|
||||||
* @return The deferred promise.
|
* @return The deferred promise.
|
||||||
*/
|
*/
|
||||||
promiseDefer<T>(): PromiseDefer<T> {
|
promiseDefer<T>(): PromiseDefer<T> {
|
||||||
const deferred: PromiseDefer<T> = {};
|
const deferred: Partial<PromiseDefer<T>> = {};
|
||||||
deferred.promise = new Promise((resolve, reject): void => {
|
deferred.promise = new Promise((resolve, reject): void => {
|
||||||
deferred.resolve = resolve;
|
deferred.resolve = resolve;
|
||||||
deferred.reject = reject;
|
deferred.reject = reject;
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred;
|
return deferred as PromiseDefer<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1257,7 +1263,11 @@ export class CoreUtilsProvider {
|
||||||
* @param key Key to check.
|
* @param key Key to check.
|
||||||
* @return Whether the two objects/arrays have the same value (or lack of one) for a given key.
|
* @return Whether the two objects/arrays have the same value (or lack of one) for a given key.
|
||||||
*/
|
*/
|
||||||
sameAtKeyMissingIsBlank(obj1: unknown, obj2: unknown, key: string): boolean {
|
sameAtKeyMissingIsBlank(
|
||||||
|
obj1: Record<string, unknown> | unknown[],
|
||||||
|
obj2: Record<string, unknown> | unknown[],
|
||||||
|
key: string,
|
||||||
|
): boolean {
|
||||||
let value1 = typeof obj1[key] != 'undefined' ? obj1[key] : '';
|
let value1 = typeof obj1[key] != 'undefined' ? obj1[key] : '';
|
||||||
let value2 = typeof obj2[key] != 'undefined' ? obj2[key] : '';
|
let value2 = typeof obj2[key] != 'undefined' ? obj2[key] : '';
|
||||||
|
|
||||||
|
@ -1426,19 +1436,19 @@ export class CoreUtilsProvider {
|
||||||
* @return Array without duplicate values.
|
* @return Array without duplicate values.
|
||||||
*/
|
*/
|
||||||
uniqueArray<T>(array: T[], key?: string): T[] {
|
uniqueArray<T>(array: T[], key?: string): T[] {
|
||||||
const filtered = [];
|
|
||||||
const unique = {}; // Use an object to make it faster to check if it's duplicate.
|
const unique = {}; // Use an object to make it faster to check if it's duplicate.
|
||||||
|
|
||||||
array.forEach((entry) => {
|
return array.filter(entry => {
|
||||||
const value = key ? entry[key] : entry;
|
const value = key ? entry[key] : entry;
|
||||||
|
|
||||||
if (!unique[value]) {
|
if (value in unique) {
|
||||||
unique[value] = true;
|
unique[value] = true;
|
||||||
filtered.push(entry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return filtered;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1487,7 +1497,14 @@ export class CoreUtilsProvider {
|
||||||
*
|
*
|
||||||
* @return Promise resolved with the QR string, rejected if error or cancelled.
|
* @return Promise resolved with the QR string, rejected if error or cancelled.
|
||||||
*/
|
*/
|
||||||
async startScanQR(): Promise<string> {
|
async startScanQR(): Promise<string | undefined> {
|
||||||
|
try {
|
||||||
|
return this.startScanQR();
|
||||||
|
} catch (error) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!CoreApp.instance.isMobile()) {
|
if (!CoreApp.instance.isMobile()) {
|
||||||
return Promise.reject('QRScanner isn\'t available in desktop apps.');
|
return Promise.reject('QRScanner isn\'t available in desktop apps.');
|
||||||
}
|
}
|
||||||
|
@ -1613,21 +1630,21 @@ export type PromiseDefer<T> = {
|
||||||
/**
|
/**
|
||||||
* The promise.
|
* The promise.
|
||||||
*/
|
*/
|
||||||
promise?: Promise<T>;
|
promise: Promise<T>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to resolve the promise.
|
* Function to resolve the promise.
|
||||||
*
|
*
|
||||||
* @param value The resolve value.
|
* @param value The resolve value.
|
||||||
*/
|
*/
|
||||||
resolve?: (value?: T) => void; // Function to resolve the promise.
|
resolve: (value?: T) => void; // Function to resolve the promise.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to reject the promise.
|
* Function to reject the promise.
|
||||||
*
|
*
|
||||||
* @param reason The reject param.
|
* @param reason The reject param.
|
||||||
*/
|
*/
|
||||||
reject?: (reason?: unknown) => void;
|
reject: (reason?: unknown) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,6 +15,12 @@
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { environment } from '@/environments/environment';
|
import { environment } from '@/environments/environment';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log function type.
|
||||||
|
*/
|
||||||
|
type LogFunction = (...data: unknown[]) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper service to display messages in the console.
|
* Helper service to display messages in the console.
|
||||||
*
|
*
|
||||||
|
@ -34,9 +40,13 @@ export class CoreLogger {
|
||||||
debug: LogFunction;
|
debug: LogFunction;
|
||||||
error: LogFunction;
|
error: LogFunction;
|
||||||
|
|
||||||
// Avoid creating singleton instances.
|
// Avoid creating instances.
|
||||||
private constructor() {
|
private constructor(log: LogFunction, info: LogFunction, warn: LogFunction, debug: LogFunction, error: LogFunction) {
|
||||||
// Nothing to do.
|
this.log = log;
|
||||||
|
this.info = info;
|
||||||
|
this.warn = warn;
|
||||||
|
this.debug = debug;
|
||||||
|
this.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,29 +64,23 @@ export class CoreLogger {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
const muted = () => {};
|
const muted = () => {};
|
||||||
|
|
||||||
return {
|
return new CoreLogger(muted, muted, muted, muted, muted);
|
||||||
log: muted,
|
|
||||||
info: muted,
|
|
||||||
warn: muted,
|
|
||||||
debug: muted,
|
|
||||||
error: muted,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
className = className || '';
|
className = className || '';
|
||||||
|
|
||||||
return {
|
return new CoreLogger(
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
log: CoreLogger.prepareLogFn(console.log.bind(console), className),
|
CoreLogger.prepareLogFn(console.log.bind(console), className),
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
info: CoreLogger.prepareLogFn(console.info.bind(console), className),
|
CoreLogger.prepareLogFn(console.info.bind(console), className),
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
warn: CoreLogger.prepareLogFn(console.warn.bind(console), className),
|
CoreLogger.prepareLogFn(console.warn.bind(console), className),
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
debug: CoreLogger.prepareLogFn(console.debug.bind(console), className),
|
CoreLogger.prepareLogFn(console.debug.bind(console), className),
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
error: CoreLogger.prepareLogFn(console.error.bind(console), className),
|
CoreLogger.prepareLogFn(console.error.bind(console), className),
|
||||||
};
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,8 +100,3 @@ export class CoreLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Log function type.
|
|
||||||
*/
|
|
||||||
type LogFunction = (...data: unknown[]) => void;
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictPropertyInitialization": true,
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
|
|
Loading…
Reference in New Issue