MOBILE-3320 lint: Update linting rules

main
Noel De Martin 2020-10-14 17:20:15 +02:00
parent 58e7a78ee7
commit 43946fb61e
6 changed files with 201 additions and 121 deletions

View File

@ -15,6 +15,7 @@ module.exports = {
'prettier/@typescript-eslint', 'prettier/@typescript-eslint',
'plugin:jest/recommended', 'plugin:jest/recommended',
'plugin:@angular-eslint/recommended', 'plugin:@angular-eslint/recommended',
'plugin:promise/recommended',
], ],
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
parserOptions: { parserOptions: {
@ -22,11 +23,12 @@ module.exports = {
sourceType: 'module', sourceType: 'module',
}, },
plugins: [ plugins: [
'eslint-plugin-prefer-arrow',
'eslint-plugin-jsdoc',
'@typescript-eslint', '@typescript-eslint',
'header', 'header',
'jest', 'jest',
'jsdoc',
'prefer-arrow',
'promise',
], ],
rules: { rules: {
'@angular-eslint/component-class-suffix': ['error', { suffixes: ['Component', 'Page'] }], '@angular-eslint/component-class-suffix': ['error', { suffixes: ['Component', 'Page'] }],
@ -56,7 +58,29 @@ module.exports = {
accessibility: 'no-public', accessibility: 'no-public',
}, },
], ],
'@typescript-eslint/indent': 'off', '@typescript-eslint/explicit-module-boundary-types': [
'error',
{
allowArgumentsExplicitlyTypedAsAny: true,
},
],
'@typescript-eslint/indent': [
'error',
4,
{
SwitchCase: 1,
ignoredNodes: [
'ClassProperty *',
],
},
],
'@typescript-eslint/lines-between-class-members': [
'error',
'always',
{
exceptAfterSingleLine: true,
},
],
'@typescript-eslint/member-delimiter-style': [ '@typescript-eslint/member-delimiter-style': [
'error', 'error',
{ {
@ -104,18 +128,6 @@ module.exports = {
'always', 'always',
], ],
'@typescript-eslint/type-annotation-spacing': 'error', '@typescript-eslint/type-annotation-spacing': 'error',
'@typescript-eslint/typedef': [
'error',
{
arrayDestructuring: false,
arrowParameter: false,
memberVariableDeclaration: true,
objectDestructuring: false,
parameter: true,
propertyDeclaration: true,
variableDeclaration: false,
},
],
'@typescript-eslint/unified-signatures': 'error', '@typescript-eslint/unified-signatures': 'error',
'header/header': [ 'header/header': [
2, 2,
@ -137,12 +149,20 @@ module.exports = {
], ],
1, 1,
], ],
'promise/catch-or-return': [
'warn',
{
allowFinally: true,
},
],
'arrow-body-style': ['error', 'as-needed'], 'arrow-body-style': ['error', 'as-needed'],
'array-bracket-spacing': ['error', 'never'], 'array-bracket-spacing': ['error', 'never'],
'comma-dangle': ['error', 'always-multiline'], 'comma-dangle': ['error', 'always-multiline'],
'constructor-super': 'error', 'constructor-super': 'error',
'curly': 'error', 'curly': 'error',
'eol-last': 'error', 'eol-last': 'error',
'function-call-argument-newline': ['error', 'consistent'],
'function-paren-newline': ['error', 'multiline-arguments'],
'id-blacklist': [ 'id-blacklist': [
'error', 'error',
'any', 'any',

6
package-lock.json generated
View File

@ -7826,6 +7826,12 @@
"integrity": "sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ==", "integrity": "sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ==",
"dev": true "dev": true
}, },
"eslint-plugin-promise": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz",
"integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==",
"dev": true
},
"eslint-scope": { "eslint-scope": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",

View File

@ -130,6 +130,7 @@
"eslint-plugin-jest": "^24.1.0", "eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsdoc": "^30.6.3", "eslint-plugin-jsdoc": "^30.6.3",
"eslint-plugin-prefer-arrow": "^1.2.2", "eslint-plugin-prefer-arrow": "^1.2.2",
"eslint-plugin-promise": "^4.2.1",
"faker": "^5.1.0", "faker": "^5.1.0",
"jest": "^26.5.0", "jest": "^26.5.0",
"jest-preset-angular": "^8.3.1", "jest-preset-angular": "^8.3.1",

View File

@ -51,25 +51,9 @@ export class CoreIframeUtilsProvider {
constructor() { constructor() {
this.logger = CoreLogger.getInstance('CoreUtilsProvider'); this.logger = CoreLogger.getInstance('CoreUtilsProvider');
const win = <WKUserScriptWindow> window; if (CoreApp.instance.isIOS() && 'WKUserScript' in window) {
// eslint-disable-next-line promise/catch-or-return
if (CoreApp.instance.isIOS() && win.WKUserScript) { Platform.instance.ready().then(() => this.injectiOSScripts(window));
Platform.instance.ready().then(() => {
// Inject code to the iframes because we cannot access the online ones.
const wwwPath = CoreFile.instance.getWWWAbsolutePath();
const linksPath = CoreTextUtils.instance.concatenatePaths(wwwPath, 'assets/js/iframe-treat-links.js');
const recaptchaPath = CoreTextUtils.instance.concatenatePaths(wwwPath, 'assets/js/iframe-recaptcha.js');
win.WKUserScript.addScript({ id: 'CoreIframeUtilsLinksScript', file: linksPath });
win.WKUserScript.addScript({
id: 'CoreIframeUtilsRecaptchaScript',
file: recaptchaPath,
injectionTime: WKUserScriptInjectionTime.END,
});
// Handle post messages received by iframes.
window.addEventListener('message', this.handleIframeMessage.bind(this));
});
} }
} }
@ -214,11 +198,15 @@ export class CoreIframeUtilsProvider {
* @param contentDocument The document of the element contents. * @param contentDocument The document of the element contents.
* @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.
*/ */
redefineWindowOpen(element: CoreFrameElement, contentWindow: Window, contentDocument: Document, redefineWindowOpen(
navCtrl?: NavController): void { element: CoreFrameElement,
contentWindow: Window,
contentDocument: Document,
navCtrl?: NavController,
): void {
if (contentWindow) { if (contentWindow) {
// Intercept window.open. // Intercept window.open.
contentWindow.open = (url: string, name: string): Window => { contentWindow.open = (url: string, name: string) => {
this.windowOpen(url, name, element, navCtrl); this.windowOpen(url, name, element, navCtrl);
return null; return null;
@ -387,8 +375,11 @@ export class CoreIframeUtilsProvider {
* @param event Click event. * @param event Click event.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
protected async linkClicked(link: {href: string; target?: string}, element?: HTMLFrameElement | HTMLObjectElement, protected async linkClicked(
event?: Event): Promise<void> { link: {href: string; target?: string},
element?: HTMLFrameElement | HTMLObjectElement,
event?: Event,
): Promise<void> {
if (event && event.defaultPrevented) { if (event && event.defaultPrevented) {
// Event already prevented by some other code. // Event already prevented by some other code.
return; return;
@ -454,6 +445,27 @@ export class CoreIframeUtilsProvider {
} }
} }
/**
* Inject code to the iframes because we cannot access the online ones.
*
* @param userScriptWindow Window.
*/
private injectiOSScripts(userScriptWindow: WKUserScriptWindow) {
const wwwPath = CoreFile.instance.getWWWAbsolutePath();
const linksPath = CoreTextUtils.instance.concatenatePaths(wwwPath, 'assets/js/iframe-treat-links.js');
const recaptchaPath = CoreTextUtils.instance.concatenatePaths(wwwPath, 'assets/js/iframe-recaptcha.js');
userScriptWindow.WKUserScript.addScript({ id: 'CoreIframeUtilsLinksScript', file: linksPath });
userScriptWindow.WKUserScript.addScript({
id: 'CoreIframeUtilsRecaptchaScript',
file: recaptchaPath,
injectionTime: WKUserScriptInjectionTime.END,
});
// Handle post messages received by iframes.
window.addEventListener('message', this.handleIframeMessage.bind(this));
}
} }
export class CoreIframeUtils extends makeSingleton(CoreIframeUtilsProvider) {} export class CoreIframeUtils extends makeSingleton(CoreIframeUtilsProvider) {}

View File

@ -450,8 +450,17 @@ export class CoreTextUtilsProvider {
* @param courseId Course ID the text belongs to. It can be used to improve performance with filters. * @param courseId Course ID the text belongs to. It can be used to improve performance with filters.
* @deprecated since 3.8.3. Please use viewText instead. * @deprecated since 3.8.3. Please use viewText instead.
*/ */
expandText(title: string, text: string, component?: string, componentId?: string | number, files?: CoreWSExternalFile[], expandText(
filter?: boolean, contextLevel?: string, instanceId?: number, courseId?: number): void { title: string,
text: string,
component?: string,
componentId?: string | number,
files?: CoreWSExternalFile[],
filter?: boolean,
contextLevel?: string,
instanceId?: number,
courseId?: number,
): void {
return this.viewText(title, text, { return this.viewText(title, text, {
component, component,
componentId, componentId,

View File

@ -48,12 +48,8 @@ export class CoreUtilsProvider {
constructor(protected zone: NgZone) { constructor(protected zone: NgZone) {
this.logger = CoreLogger.getInstance('CoreUtilsProvider'); this.logger = CoreLogger.getInstance('CoreUtilsProvider');
Platform.instance.ready().then(() => { // eslint-disable-next-line promise/catch-or-return
if (window.cordova && window.cordova.InAppBrowser) { Platform.instance.ready().then(() => this.overrideWindowOpen());
// Override the default window.open with the InAppBrowser one.
window.open = window.cordova.InAppBrowser.open;
}
});
} }
/** /**
@ -76,7 +72,7 @@ export class CoreUtilsProvider {
if (!this.isWebServiceError(error)) { if (!this.isWebServiceError(error)) {
// Local error. Add an extra warning. // Local error. Add an extra warning.
errorMessage += '<br><br>' + Translate.instance.instant('core.errorsomedatanotdownloaded'); errorMessage += '<br><br>' + Translate.instance.instant('core.errorsomedatanotdownloaded');
} }
return errorMessage; return errorMessage;
@ -88,35 +84,25 @@ export class CoreUtilsProvider {
* @param promises Promises. * @param promises Promises.
* @return Promise resolved if all promises are resolved and rejected if at least 1 promise fails. * @return Promise resolved if all promises are resolved and rejected if at least 1 promise fails.
*/ */
allPromises(promises: Promise<unknown>[]): Promise<void> { async allPromises(promises: Promise<unknown>[]): Promise<void> {
if (!promises || !promises.length) { if (!promises || !promises.length) {
return Promise.resolve(); return Promise.resolve();
} }
return new Promise((resolve, reject): void => { const getPromiseError = async (promise): Promise<Error | void> => {
const total = promises.length; try {
let count = 0; await promise;
let hasFailed = false; } catch (error) {
let error; return error;
}
};
promises.forEach((promise) => { const errors = await Promise.all(promises.map(getPromiseError));
promise.catch((err) => { const error = errors.find(error => !!error);
hasFailed = true;
error = err;
}).finally(() => {
count++;
if (count === total) { if (error) {
// All promises have finished, reject/resolve. throw error;
if (hasFailed) { }
reject(error);
} else {
resolve();
}
}
});
});
});
} }
/** /**
@ -151,9 +137,13 @@ export class CoreUtilsProvider {
* @param undefinedIsNull True if undefined is equal to null. Defaults to true. * @param undefinedIsNull True if undefined is equal to null. Defaults to true.
* @return Whether both items are equal. * @return Whether both items are equal.
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types basicLeftCompare(
basicLeftCompare(itemA: any, itemB: any, maxLevels: number = 0, itemA: any, // eslint-disable-line @typescript-eslint/no-explicit-any
level: number = 0, undefinedIsNull: boolean = true): boolean { itemB: any, // eslint-disable-line @typescript-eslint/no-explicit-any
maxLevels: number = 0,
level: number = 0,
undefinedIsNull: boolean = true,
): boolean {
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') {
@ -467,19 +457,24 @@ export class CoreUtilsProvider {
* @param ...args All the params sent after checkAll will be passed to isEnabledFn. * @param ...args All the params sent after checkAll will be passed to isEnabledFn.
* @return Promise resolved with the list of enabled sites. * @return Promise resolved with the list of enabled sites.
*/ */
filterEnabledSites<P extends []>(siteIds: string[], isEnabledFn: (siteId, ...args: P) => boolean | Promise<boolean>, filterEnabledSites<P extends unknown[]>(
checkAll?: boolean, ...args: P): Promise<string[]> { siteIds: string[],
isEnabledFn: (siteId, ...args: P) => boolean | Promise<boolean>,
checkAll?: boolean,
...args: P
): Promise<string[]> {
const promises = []; const promises = [];
const enabledSites = []; const enabledSites = [];
for (const i in siteIds) { for (const i in siteIds) {
const siteId = siteIds[i]; const siteId = siteIds[i];
const pushIfEnabled = enabled => enabled && enabledSites.push(siteId);
if (checkAll || !promises.length) { if (checkAll || !promises.length) {
promises.push(Promise.resolve(isEnabledFn.apply(isEnabledFn, [siteId].concat(args))).then((enabled) => { promises.push(
if (enabled) { Promise
enabledSites.push(siteId); .resolve(isEnabledFn(siteId, ...args))
} .then(pushIfEnabled),
})); );
} }
} }
@ -527,8 +522,13 @@ export class CoreUtilsProvider {
* @param maxDepth Max Depth to convert to tree. Children found will be in the last level of depth. * @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. * @return Array with the formatted tree, children will be on each node under children field.
*/ */
formatTree<T>(list: T[], parentFieldName: string = 'parent', idFieldName: string = 'id', rootParentId: number = 0, formatTree<T>(
maxDepth: number = 5): TreeNode<T>[] { list: T[],
parentFieldName: string = 'parent',
idFieldName: string = 'id',
rootParentId: number = 0,
maxDepth: number = 5,
): TreeNode<T>[] {
const map = {}; const map = {};
const mapDepth = {}; const mapDepth = {};
const tree: TreeNode<T>[] = []; const tree: TreeNode<T>[] = [];
@ -649,7 +649,7 @@ export class CoreUtilsProvider {
if (fallbackLang === defaultLang) { if (fallbackLang === defaultLang) {
// Same language, just reject. // Same language, just reject.
return Promise.reject('Countries not found.'); throw new Error('Countries not found.');
} }
return this.getCountryKeysListForLanguage(fallbackLang); return this.getCountryKeysListForLanguage(fallbackLang);
@ -786,7 +786,7 @@ export class CoreUtilsProvider {
* @param value Value to check. * @param value Value to check.
* @return Whether the value is false, 0 or "0". * @return Whether the value is false, 0 or "0".
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/no-explicit-any
isFalseOrZero(value: any): boolean { isFalseOrZero(value: any): boolean {
return typeof value != 'undefined' && (value === false || value === 'false' || parseInt(value, 10) === 0); return typeof value != 'undefined' && (value === false || value === 'false' || parseInt(value, 10) === 0);
} }
@ -797,7 +797,7 @@ export class CoreUtilsProvider {
* @param value Value to check. * @param value Value to check.
* @return Whether the value is true, 1 or "1". * @return Whether the value is true, 1 or "1".
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/no-explicit-any
isTrueOrOne(value: any): boolean { isTrueOrOne(value: any): boolean {
return typeof value != 'undefined' && (value === true || value === 'true' || parseInt(value, 10) === 1); return typeof value != 'undefined' && (value === true || value === 'true' || parseInt(value, 10) === 1);
} }
@ -808,7 +808,7 @@ export class CoreUtilsProvider {
* @param error Error to check. * @param error Error to check.
* @return Whether the error was returned by the WebService. * @return Whether the error was returned by the WebService.
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/no-explicit-any
isWebServiceError(error: any): boolean { isWebServiceError(error: any): boolean {
return error && (typeof error.warningcode != 'undefined' || (typeof error.errorcode != 'undefined' && return error && (typeof error.warningcode != 'undefined' || (typeof error.errorcode != 'undefined' &&
error.errorcode != 'invalidtoken' && error.errorcode != 'userdeleted' && error.errorcode != 'upgraderunning' && error.errorcode != 'invalidtoken' && error.errorcode != 'userdeleted' && error.errorcode != 'upgraderunning' &&
@ -827,8 +827,12 @@ export class CoreUtilsProvider {
* @param defaultValue Element that will become default option value. Default 0. * @param defaultValue Element that will become default option value. Default 0.
* @return The now assembled array * @return The now assembled array
*/ */
makeMenuFromList<T>(list: string, defaultLabel?: string, separator: string = ',', makeMenuFromList<T>(
defaultValue?: T): { label: string; value: T | number }[] { list: string,
defaultLabel?: string,
separator: string = ',',
defaultValue?: T,
): { label: string; value: T | number }[] {
// Split and format the list. // Split and format the list.
const split = list.split(separator).map((label, index) => ({ const split = list.split(separator).map((label, index) => ({
label: label.trim(), label: label.trim(),
@ -863,7 +867,7 @@ export class CoreUtilsProvider {
* @param value Value to check. * @param value Value to check.
* @return True if not null and not undefined. * @return True if not null and not undefined.
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types // eslint-disable-next-line @typescript-eslint/no-explicit-any
notNullOrUndefined(value: any): boolean { notNullOrUndefined(value: any): boolean {
return typeof value != 'undefined' && value !== null; return typeof value != 'undefined' && value !== null;
} }
@ -1008,36 +1012,32 @@ export class CoreUtilsProvider {
* @param url The URL of the file. * @param url The URL of the file.
* @return Promise resolved when opened. * @return Promise resolved when opened.
*/ */
openOnlineFile(url: string): Promise<void> { async openOnlineFile(url: string): Promise<void> {
if (CoreApp.instance.isAndroid()) { if (CoreApp.instance.isAndroid()) {
// In Android we need the mimetype to open it. // In Android we need the mimetype to open it.
return this.getMimeTypeFromUrl(url).catch(() => { const mimetype = await this.ignoreErrors(this.getMimeTypeFromUrl(url));
// Error getting mimetype, return undefined.
}).then((mimetype) => {
if (!mimetype) {
// Couldn't retrieve mimetype. Return error.
return Promise.reject(Translate.instance.instant('core.erroropenfilenoextension'));
}
const options = { if (!mimetype) {
action: WebIntent.instance.ACTION_VIEW, // Couldn't retrieve mimetype. Return error.
url, throw new Error(Translate.instance.instant('core.erroropenfilenoextension'));
type: mimetype, }
};
return WebIntent.instance.startActivity(options).catch((error) => { const options = {
this.logger.error('Error opening online file ' + url + ' with mimetype ' + mimetype); action: WebIntent.instance.ACTION_VIEW,
this.logger.error('Error: ', JSON.stringify(error)); url,
type: mimetype,
};
return Promise.reject(Translate.instance.instant('core.erroropenfilenoapp')); return WebIntent.instance.startActivity(options).catch((error) => {
}); this.logger.error('Error opening online file ' + url + ' with mimetype ' + mimetype);
this.logger.error('Error: ', JSON.stringify(error));
throw new Error(Translate.instance.instant('core.erroropenfilenoapp'));
}); });
} }
// In the rest of platforms we need to open them in InAppBrowser. // In the rest of platforms we need to open them in InAppBrowser.
this.openInApp(url); this.openInApp(url);
return Promise.resolve();
} }
/** /**
@ -1062,8 +1062,13 @@ export class CoreUtilsProvider {
* @param sortByValue True to sort values alphabetically, false otherwise. * @param sortByValue True to sort values alphabetically, false otherwise.
* @return Array of objects with the name & value of each property. * @return Array of objects with the name & value of each property.
*/ */
objectToArrayOfObjects(obj: Record<string, unknown>, keyName: string, valueName: string, sortByKey?: boolean, objectToArrayOfObjects(
sortByValue?: boolean): Record<string, unknown>[] { obj: Record<string, unknown>,
keyName: string,
valueName: string,
sortByKey?: boolean,
sortByValue?: boolean,
): Record<string, unknown>[] {
// Get the entries from an object or primitive value. // Get the entries from an object or primitive value.
const getEntries = (elKey: string, value: unknown): Record<string, unknown>[] | unknown => { const getEntries = (elKey: string, value: unknown): Record<string, unknown>[] | unknown => {
if (typeof value == 'undefined' || value == null) { if (typeof value == 'undefined' || value == null) {
@ -1123,8 +1128,12 @@ export class CoreUtilsProvider {
* @param keyPrefix Key prefix if neededs to delete it. * @param keyPrefix Key prefix if neededs to delete it.
* @return Object. * @return Object.
*/ */
objectToKeyValueMap(objects: Record<string, unknown>[], keyName: string, valueName: string, objectToKeyValueMap(
keyPrefix?: string): {[name: string]: unknown} { objects: Record<string, unknown>[],
keyName: string,
valueName: string,
keyPrefix?: string,
): {[name: string]: unknown} {
if (!objects) { if (!objects) {
return; return;
} }
@ -1343,13 +1352,25 @@ export class CoreUtilsProvider {
*/ */
timeoutPromise<T>(promise: Promise<T>, time: number): Promise<T> { timeoutPromise<T>(promise: Promise<T>, time: number): Promise<T> {
return new Promise((resolve, reject): void => { return new Promise((resolve, reject): void => {
const timeout = setTimeout(() => { let timedOut = false;
reject({ timeout: true }); const resolveBeforeTimeout = () => {
}, time); if (timedOut) {
return;
}
resolve();
};
const timeout = setTimeout(
() => {
reject({ timeout: true });
timedOut = true;
},
time,
);
promise.then(resolve).catch(reject).finally(() => { promise
clearTimeout(timeout); .then(resolveBeforeTimeout)
}); .catch(reject)
.finally(() => clearTimeout(timeout));
}); });
} }
@ -1362,7 +1383,7 @@ export class CoreUtilsProvider {
* @param strict If true, then check the input and return false if it is not a valid number. * @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. * @return False if bad format, empty string if empty value or the parsed float if not.
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
unformatFloat(localeFloat: any, strict?: boolean): false | '' | number { unformatFloat(localeFloat: any, strict?: boolean): false | '' | number {
// Bad format on input type number. // Bad format on input type number.
if (typeof localeFloat == 'undefined') { if (typeof localeFloat == 'undefined') {
@ -1568,6 +1589,17 @@ export class CoreUtilsProvider {
return new Promise(resolve => setTimeout(resolve, milliseconds)); return new Promise(resolve => setTimeout(resolve, milliseconds));
} }
/**
* Override native window.open with InAppBrowser if available.
*/
private overrideWindowOpen() {
if (!window.cordova?.InAppBrowser) {
return;
}
window.open = window.cordova.InAppBrowser.open;
}
} }
export class CoreUtils extends makeSingleton(CoreUtilsProvider) {} export class CoreUtils extends makeSingleton(CoreUtilsProvider) {}