Merge pull request #2557 from NoelDeMartin/MOBILE-3320
MOBILE-3320 Updated linting rules + add Error testsmain
commit
1ab5c78491
506
.eslintrc.js
506
.eslintrc.js
|
@ -1,244 +1,274 @@
|
||||||
|
var appConfig = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es6: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'@typescript-eslint',
|
||||||
|
'header',
|
||||||
|
'jsdoc',
|
||||||
|
'prefer-arrow',
|
||||||
|
'promise',
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'prettier',
|
||||||
|
'prettier/@typescript-eslint',
|
||||||
|
'plugin:@angular-eslint/recommended',
|
||||||
|
'plugin:promise/recommended',
|
||||||
|
],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
reportUnusedDisableDirectives: true,
|
||||||
|
rules: {
|
||||||
|
'@angular-eslint/component-class-suffix': ['error', { suffixes: ['Component', 'Page'] }],
|
||||||
|
'@typescript-eslint/adjacent-overload-signatures': 'error',
|
||||||
|
'@typescript-eslint/ban-types': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
types: {
|
||||||
|
Boolean: {
|
||||||
|
message: 'Use \'boolean\' instead.',
|
||||||
|
},
|
||||||
|
Number: {
|
||||||
|
message: 'Use \'number\' instead.',
|
||||||
|
},
|
||||||
|
String: {
|
||||||
|
message: 'Use \'string\' instead.',
|
||||||
|
},
|
||||||
|
Object: {
|
||||||
|
message: 'Use {} instead.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/explicit-member-accessibility': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
accessibility: 'no-public',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@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': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
multiline: {
|
||||||
|
delimiter: 'semi',
|
||||||
|
requireLast: true,
|
||||||
|
},
|
||||||
|
singleline: {
|
||||||
|
delimiter: 'semi',
|
||||||
|
requireLast: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/member-ordering': 'error',
|
||||||
|
'@typescript-eslint/naming-convention': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
selector: 'property',
|
||||||
|
modifiers: ['readonly'],
|
||||||
|
format: ['UPPER_CASE'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'property',
|
||||||
|
format: ['camelCase'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-empty-function': 'error',
|
||||||
|
'@typescript-eslint/no-empty-interface': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'@typescript-eslint/no-inferrable-types': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
ignoreParameters: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/no-this-alias': 'error',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'error',
|
||||||
|
'@typescript-eslint/quotes': [
|
||||||
|
'error',
|
||||||
|
'single',
|
||||||
|
],
|
||||||
|
'@typescript-eslint/semi': [
|
||||||
|
'error',
|
||||||
|
'always',
|
||||||
|
],
|
||||||
|
'@typescript-eslint/type-annotation-spacing': 'error',
|
||||||
|
'@typescript-eslint/unified-signatures': 'error',
|
||||||
|
'header/header': [
|
||||||
|
2,
|
||||||
|
'line',
|
||||||
|
[
|
||||||
|
' (C) Copyright 2015 Moodle Pty Ltd.',
|
||||||
|
'',
|
||||||
|
' Licensed under the Apache License, Version 2.0 (the "License");',
|
||||||
|
' you may not use this file except in compliance with the License.',
|
||||||
|
' You may obtain a copy of the License at',
|
||||||
|
'',
|
||||||
|
' http://www.apache.org/licenses/LICENSE-2.0',
|
||||||
|
'',
|
||||||
|
' Unless required by applicable law or agreed to in writing, software',
|
||||||
|
' distributed under the License is distributed on an "AS IS" BASIS,',
|
||||||
|
' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
|
||||||
|
' See the License for the specific language governing permissions and',
|
||||||
|
' limitations under the License.',
|
||||||
|
],
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'promise/catch-or-return': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
allowFinally: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'arrow-body-style': ['error', 'as-needed'],
|
||||||
|
'array-bracket-spacing': ['error', 'never'],
|
||||||
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
|
'constructor-super': 'error',
|
||||||
|
'curly': 'error',
|
||||||
|
'eol-last': 'error',
|
||||||
|
'function-call-argument-newline': ['error', 'consistent'],
|
||||||
|
'function-paren-newline': ['error', 'multiline-arguments'],
|
||||||
|
'id-blacklist': [
|
||||||
|
'error',
|
||||||
|
'any',
|
||||||
|
'Number',
|
||||||
|
'number',
|
||||||
|
'String',
|
||||||
|
'string',
|
||||||
|
'Boolean',
|
||||||
|
'boolean',
|
||||||
|
'Undefined',
|
||||||
|
'undefined',
|
||||||
|
],
|
||||||
|
'id-match': 'error',
|
||||||
|
'jsdoc/check-alignment': 'error',
|
||||||
|
'jsdoc/check-indentation': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
excludeTags: ['param'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'jsdoc/newline-after-description': 'error',
|
||||||
|
'linebreak-style': [
|
||||||
|
'error',
|
||||||
|
'unix',
|
||||||
|
],
|
||||||
|
'max-len': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
code: 132,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'new-parens': 'error',
|
||||||
|
'no-bitwise': 'error',
|
||||||
|
'no-cond-assign': 'error',
|
||||||
|
'no-console': 'error',
|
||||||
|
'no-debugger': 'error',
|
||||||
|
'no-duplicate-case': 'error',
|
||||||
|
'no-duplicate-imports': 'error',
|
||||||
|
'no-empty': 'error',
|
||||||
|
'no-eval': 'error',
|
||||||
|
'no-invalid-this': 'error',
|
||||||
|
'no-irregular-whitespace': 'error',
|
||||||
|
'no-multiple-empty-lines': 'error',
|
||||||
|
'no-new-wrappers': 'error',
|
||||||
|
'no-redeclare': 'error',
|
||||||
|
'no-sequences': 'error',
|
||||||
|
'no-trailing-spaces': 'error',
|
||||||
|
'no-underscore-dangle': 'error',
|
||||||
|
'no-unused-labels': 'error',
|
||||||
|
'no-var': 'error',
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
'one-var': ['error', 'never'],
|
||||||
|
'padded-blocks': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
classes: 'always',
|
||||||
|
blocks: 'never',
|
||||||
|
switches: 'never',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'padding-line-between-statements': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
blankLine: 'always',
|
||||||
|
prev: '*',
|
||||||
|
next: 'return',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'prefer-arrow/prefer-arrow-functions': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
singleReturnOnly: true,
|
||||||
|
allowStandaloneDeclarations: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'prefer-const': 'error',
|
||||||
|
'prefer-spread': 'off',
|
||||||
|
'quote-props': [
|
||||||
|
'error',
|
||||||
|
'consistent-as-needed',
|
||||||
|
],
|
||||||
|
'spaced-comment': [
|
||||||
|
'error',
|
||||||
|
'always',
|
||||||
|
{
|
||||||
|
markers: [
|
||||||
|
'/',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'use-isnan': 'error',
|
||||||
|
'yoda': 'error',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var testsConfig = Object.assign({}, appConfig);
|
||||||
|
testsConfig['rules']['padded-blocks'] = [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
classes: 'always',
|
||||||
|
switches: 'never',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
testsConfig['plugins'].push('jest');
|
||||||
|
testsConfig['extends'].push('plugin:jest/recommended');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
Object.assign({ files: ['*.ts'] }, appConfig),
|
||||||
files: ['*.ts'],
|
Object.assign({ files: ['*.test.ts'] }, testsConfig),
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es6: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'prettier',
|
|
||||||
'prettier/@typescript-eslint',
|
|
||||||
'plugin:jest/recommended',
|
|
||||||
'plugin:@angular-eslint/recommended',
|
|
||||||
],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: {
|
|
||||||
project: 'tsconfig.json',
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
'eslint-plugin-prefer-arrow',
|
|
||||||
'eslint-plugin-jsdoc',
|
|
||||||
'@typescript-eslint',
|
|
||||||
'header',
|
|
||||||
'jest',
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'@angular-eslint/component-class-suffix': ['error', { suffixes: ['Component', 'Page'] }],
|
|
||||||
'@typescript-eslint/adjacent-overload-signatures': 'error',
|
|
||||||
'@typescript-eslint/ban-types': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
types: {
|
|
||||||
Boolean: {
|
|
||||||
message: 'Use \'boolean\' instead.',
|
|
||||||
},
|
|
||||||
Number: {
|
|
||||||
message: 'Use \'number\' instead.',
|
|
||||||
},
|
|
||||||
String: {
|
|
||||||
message: 'Use \'string\' instead.',
|
|
||||||
},
|
|
||||||
Object: {
|
|
||||||
message: 'Use {} instead.',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@typescript-eslint/explicit-member-accessibility': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
accessibility: 'no-public',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@typescript-eslint/indent': 'error',
|
|
||||||
'@typescript-eslint/member-delimiter-style': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
multiline: {
|
|
||||||
delimiter: 'semi',
|
|
||||||
requireLast: true,
|
|
||||||
},
|
|
||||||
singleline: {
|
|
||||||
delimiter: 'semi',
|
|
||||||
requireLast: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@typescript-eslint/member-ordering': 'error',
|
|
||||||
'@typescript-eslint/naming-convention': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
selector: 'property',
|
|
||||||
modifiers: ['readonly'],
|
|
||||||
format: ['UPPER_CASE'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: 'property',
|
|
||||||
format: ['camelCase'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@typescript-eslint/no-empty-function': 'error',
|
|
||||||
'@typescript-eslint/no-empty-interface': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': [
|
|
||||||
'warn',
|
|
||||||
{
|
|
||||||
fixToUnknown: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@typescript-eslint/no-inferrable-types': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
ignoreParameters: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
|
||||||
'@typescript-eslint/no-this-alias': 'error',
|
|
||||||
'@typescript-eslint/no-unused-vars': 'error',
|
|
||||||
'@typescript-eslint/quotes': [
|
|
||||||
'error',
|
|
||||||
'single',
|
|
||||||
],
|
|
||||||
'@typescript-eslint/semi': [
|
|
||||||
'error',
|
|
||||||
'always',
|
|
||||||
],
|
|
||||||
'@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',
|
|
||||||
'header/header': [
|
|
||||||
2,
|
|
||||||
'line',
|
|
||||||
[
|
|
||||||
' (C) Copyright 2015 Moodle Pty Ltd.',
|
|
||||||
'',
|
|
||||||
' Licensed under the Apache License, Version 2.0 (the "License");',
|
|
||||||
' you may not use this file except in compliance with the License.',
|
|
||||||
' You may obtain a copy of the License at',
|
|
||||||
'',
|
|
||||||
' http://www.apache.org/licenses/LICENSE-2.0',
|
|
||||||
'',
|
|
||||||
' Unless required by applicable law or agreed to in writing, software',
|
|
||||||
' distributed under the License is distributed on an "AS IS" BASIS,',
|
|
||||||
' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
|
|
||||||
' See the License for the specific language governing permissions and',
|
|
||||||
' limitations under the License.',
|
|
||||||
],
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
'arrow-body-style': ['error', 'as-needed'],
|
|
||||||
'array-bracket-spacing': ['error', 'never'],
|
|
||||||
'comma-dangle': ['error', 'always-multiline'],
|
|
||||||
'constructor-super': 'error',
|
|
||||||
'curly': 'error',
|
|
||||||
'default-case': 'error',
|
|
||||||
'eol-last': 'error',
|
|
||||||
'id-blacklist': [
|
|
||||||
'error',
|
|
||||||
'any',
|
|
||||||
'Number',
|
|
||||||
'number',
|
|
||||||
'String',
|
|
||||||
'string',
|
|
||||||
'Boolean',
|
|
||||||
'boolean',
|
|
||||||
'Undefined',
|
|
||||||
'undefined',
|
|
||||||
],
|
|
||||||
'id-match': 'error',
|
|
||||||
'jsdoc/check-alignment': 'error',
|
|
||||||
'jsdoc/check-indentation': 'error',
|
|
||||||
'jsdoc/newline-after-description': 'error',
|
|
||||||
'linebreak-style': [
|
|
||||||
'error',
|
|
||||||
'unix',
|
|
||||||
],
|
|
||||||
'lines-between-class-members': ['error', 'always'],
|
|
||||||
'max-len': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
code: 132,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'new-parens': 'error',
|
|
||||||
'no-bitwise': 'error',
|
|
||||||
'no-cond-assign': 'error',
|
|
||||||
'no-console': 'error',
|
|
||||||
'no-debugger': 'error',
|
|
||||||
'no-duplicate-case': 'error',
|
|
||||||
'no-duplicate-imports': 'error',
|
|
||||||
'no-empty': 'error',
|
|
||||||
'no-eval': 'error',
|
|
||||||
'no-invalid-this': 'error',
|
|
||||||
'no-irregular-whitespace': 'error',
|
|
||||||
'no-multiple-empty-lines': 'error',
|
|
||||||
'no-new-wrappers': 'error',
|
|
||||||
'no-redeclare': 'error',
|
|
||||||
'no-sequences': 'error',
|
|
||||||
'no-trailing-spaces': 'error',
|
|
||||||
'no-underscore-dangle': 'error',
|
|
||||||
'no-unused-labels': 'error',
|
|
||||||
'no-var': 'error',
|
|
||||||
'object-curly-spacing': ['error', 'always'],
|
|
||||||
'one-var': ['error', 'never'],
|
|
||||||
'padded-blocks': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
classes: 'always',
|
|
||||||
blocks: 'never',
|
|
||||||
switches: 'never',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'padding-line-between-statements': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
blankLine: 'always',
|
|
||||||
prev: '*',
|
|
||||||
next: 'return',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'prefer-arrow/prefer-arrow-functions': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
singleReturnOnly: true,
|
|
||||||
allowStandaloneDeclarations: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'prefer-const': 'error',
|
|
||||||
'prefer-spread': 'off',
|
|
||||||
'quote-props': [
|
|
||||||
'error',
|
|
||||||
'consistent-as-needed',
|
|
||||||
],
|
|
||||||
'spaced-comment': [
|
|
||||||
'error',
|
|
||||||
'always',
|
|
||||||
{
|
|
||||||
markers: [
|
|
||||||
'/',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'use-isnan': 'error',
|
|
||||||
'yoda': 'error',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
files: ['*.html'],
|
files: ['*.html'],
|
||||||
extends: ['plugin:@angular-eslint/template/recommended'],
|
extends: ['plugin:@angular-eslint/template/recommended'],
|
||||||
|
|
|
@ -22,7 +22,6 @@ npm-debug.log*
|
||||||
/.sass-cache
|
/.sass-cache
|
||||||
/.sourcemaps
|
/.sourcemaps
|
||||||
/.versions
|
/.versions
|
||||||
/.vscode
|
|
||||||
/coverage
|
/coverage
|
||||||
/dist
|
/dist
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Jest All",
|
||||||
|
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||||
|
"args": ["--runInBand"],
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"internalConsoleOptions": "neverOpen",
|
||||||
|
"disableOptimisticBPs": true,
|
||||||
|
"windows": {
|
||||||
|
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Jest Current File",
|
||||||
|
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||||
|
"args": [
|
||||||
|
"${fileBasenameNoExtension}",
|
||||||
|
"--config",
|
||||||
|
"jest.config.js"
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"internalConsoleOptions": "neverOpen",
|
||||||
|
"disableOptimisticBPs": true,
|
||||||
|
"windows": {
|
||||||
|
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -3849,12 +3849,6 @@
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app-root-path": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||||
|
@ -3876,16 +3870,6 @@
|
||||||
"sprintf-js": "~1.0.2"
|
"sprintf-js": "~1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"aria-query": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
|
|
||||||
"integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"ast-types-flow": "0.0.7",
|
|
||||||
"commander": "^2.11.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"arity-n": {
|
"arity-n": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz",
|
||||||
|
@ -4059,12 +4043,6 @@
|
||||||
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
|
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ast-types-flow": {
|
|
||||||
"version": "0.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
|
|
||||||
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"astral-regex": {
|
"astral-regex": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
|
||||||
|
@ -4184,15 +4162,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
|
||||||
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
|
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
|
||||||
},
|
},
|
||||||
"axobject-query": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"ast-types-flow": "0.0.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"babel-jest": {
|
"babel-jest": {
|
||||||
"version": "26.5.2",
|
"version": "26.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.5.2.tgz",
|
||||||
|
@ -5291,60 +5260,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"codelyzer": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-cOyGQgMdhnRYtW2xrJUNrNYDjEgwQ+BrE2y93Bwz3h4DJ6vJRLfupemU5N3pbYsUlBHJf0u1j1UGk+NLW4d97g==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@angular/compiler": "9.0.0",
|
|
||||||
"@angular/core": "9.0.0",
|
|
||||||
"app-root-path": "^3.0.0",
|
|
||||||
"aria-query": "^3.0.0",
|
|
||||||
"axobject-query": "2.0.2",
|
|
||||||
"css-selector-tokenizer": "^0.7.1",
|
|
||||||
"cssauron": "^1.4.0",
|
|
||||||
"damerau-levenshtein": "^1.0.4",
|
|
||||||
"rxjs": "^6.5.3",
|
|
||||||
"semver-dsl": "^1.0.1",
|
|
||||||
"source-map": "^0.5.7",
|
|
||||||
"sprintf-js": "^1.1.2",
|
|
||||||
"tslib": "^1.10.0",
|
|
||||||
"zone.js": "~0.10.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@angular/compiler": {
|
|
||||||
"version": "9.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz",
|
|
||||||
"integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@angular/core": {
|
|
||||||
"version": "9.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz",
|
|
||||||
"integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"source-map": {
|
|
||||||
"version": "0.5.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
|
||||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"sprintf-js": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"tslib": {
|
|
||||||
"version": "1.14.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz",
|
|
||||||
"integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"collect-v8-coverage": {
|
"collect-v8-coverage": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
|
||||||
|
@ -6559,16 +6474,6 @@
|
||||||
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==",
|
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"css-selector-tokenizer": {
|
|
||||||
"version": "0.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
|
|
||||||
"integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"cssesc": "^3.0.0",
|
|
||||||
"fastparse": "^1.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"css-tree": {
|
"css-tree": {
|
||||||
"version": "1.0.0-alpha.37",
|
"version": "1.0.0-alpha.37",
|
||||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
|
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
|
||||||
|
@ -6593,15 +6498,6 @@
|
||||||
"integrity": "sha512-wHOppVDKl4vTAOWzJt5Ek37Sgd9qq1Bmj/T1OjvicWbU5W7ru7Pqbn0Jdqii3Drx/h+dixHKXNhZYx7blthL7g==",
|
"integrity": "sha512-wHOppVDKl4vTAOWzJt5Ek37Sgd9qq1Bmj/T1OjvicWbU5W7ru7Pqbn0Jdqii3Drx/h+dixHKXNhZYx7blthL7g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"cssauron": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz",
|
|
||||||
"integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"through": "X.X.X"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cssesc": {
|
"cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
|
@ -6765,12 +6661,6 @@
|
||||||
"type": "^1.0.1"
|
"type": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"damerau-levenshtein": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
|
|
||||||
"integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"dashdash": {
|
"dashdash": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||||
|
@ -7936,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",
|
||||||
|
@ -8329,6 +8225,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz",
|
||||||
"integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8="
|
"integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8="
|
||||||
},
|
},
|
||||||
|
"faker": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/faker/-/faker-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-RrWKFSSA/aNLP0g3o2WW1Zez7/MnMr7xkiZmoCfAGZmdkDQZ6l2KtuXHN5XjdvpRjDl8+3vf+Rrtl06Z352+Mw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"fast-deep-equal": {
|
"fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
@ -8363,12 +8265,6 @@
|
||||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"fastparse": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"fastq": {
|
"fastq": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
|
||||||
|
@ -15642,23 +15538,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"semver-dsl": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"semver": "^5.3.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"semver": {
|
|
||||||
"version": "5.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
|
||||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"semver-intersect": {
|
"semver-intersect": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz",
|
||||||
|
|
|
@ -121,7 +121,6 @@
|
||||||
"@angular/language-service": "~10.0.0",
|
"@angular/language-service": "~10.0.0",
|
||||||
"@ionic/angular-toolkit": "^2.3.0",
|
"@ionic/angular-toolkit": "^2.3.0",
|
||||||
"@types/node": "^12.12.64",
|
"@types/node": "^12.12.64",
|
||||||
"codelyzer": "^6.0.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "4.3.0",
|
"@typescript-eslint/eslint-plugin": "4.3.0",
|
||||||
"@typescript-eslint/parser": "4.3.0",
|
"@typescript-eslint/parser": "4.3.0",
|
||||||
"eslint": "^7.6.0",
|
"eslint": "^7.6.0",
|
||||||
|
@ -131,6 +130,8 @@
|
||||||
"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",
|
||||||
"jest": "^26.5.0",
|
"jest": "^26.5.0",
|
||||||
"jest-preset-angular": "^8.3.1",
|
"jest-preset-angular": "^8.3.1",
|
||||||
"ts-jest": "^26.4.1",
|
"ts-jest": "^26.4.1",
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
import Faker from 'faker';
|
||||||
|
|
||||||
|
import { CoreError } from './error';
|
||||||
|
|
||||||
|
describe('CoreError', () => {
|
||||||
|
|
||||||
|
it('behaves like an error', () => {
|
||||||
|
// Arrange
|
||||||
|
const message = Faker.lorem.sentence();
|
||||||
|
|
||||||
|
let error: CoreError | null = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
try {
|
||||||
|
throw new CoreError(message);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(error).not.toBeNull();
|
||||||
|
expect(error).toBeInstanceOf(Error);
|
||||||
|
expect(error).toBeInstanceOf(CoreError);
|
||||||
|
expect(error!.name).toEqual('CoreError');
|
||||||
|
expect(error!.message).toEqual(message);
|
||||||
|
expect(error!.stack).not.toBeNull();
|
||||||
|
expect(error!.stack).toContain('src/app/classes/error.test.ts');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can be subclassed', () => {
|
||||||
|
// Arrange
|
||||||
|
class CustomCoreError extends CoreError {
|
||||||
|
|
||||||
|
constructor(m: string) {
|
||||||
|
super(`Custom message: ${m}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = Faker.lorem.sentence();
|
||||||
|
|
||||||
|
let error: CustomCoreError | null = null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
try {
|
||||||
|
throw new CustomCoreError(message);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(error).not.toBeNull();
|
||||||
|
expect(error).toBeInstanceOf(Error);
|
||||||
|
expect(error).toBeInstanceOf(CoreError);
|
||||||
|
expect(error).toBeInstanceOf(CustomCoreError);
|
||||||
|
expect(error!.name).toEqual('CustomCoreError');
|
||||||
|
expect(error!.message).toEqual(`Custom message: ${message}`);
|
||||||
|
expect(error!.stack).not.toBeNull();
|
||||||
|
expect(error!.stack).toContain('src/app/classes/error.test.ts');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -13,9 +13,10 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { NavController } from '@ionic/angular';
|
||||||
import { WKUserScriptWindow, WKUserScriptInjectionTime } from 'cordova-plugin-wkuserscript';
|
import { WKUserScriptWindow, WKUserScriptInjectionTime } from 'cordova-plugin-wkuserscript';
|
||||||
|
|
||||||
import { CoreApp, CoreAppProvider } from '@services/app';
|
import { CoreApp } from '@services/app';
|
||||||
import { CoreFile } from '@services/file';
|
import { CoreFile } from '@services/file';
|
||||||
import { CoreFileHelper } from '@services/file-helper';
|
import { CoreFileHelper } from '@services/file-helper';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
|
@ -24,42 +25,35 @@ import { CoreTextUtils } from '@services/utils/text';
|
||||||
import { CoreUrlUtils } from '@services/utils/url';
|
import { CoreUrlUtils } from '@services/utils/url';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
|
||||||
import { makeSingleton, Translate, Network, Platform, NgZone } from '@singletons/core.singletons';
|
import { makeSingleton, Network, Platform, NgZone } from '@singletons/core.singletons';
|
||||||
import { CoreLogger } from '@singletons/logger';
|
import { CoreLogger } from '@singletons/logger';
|
||||||
import { CoreUrl } from '@singletons/url';
|
import { CoreUrl } from '@singletons/url';
|
||||||
import { CoreWindow } from '@singletons/window';
|
import { CoreWindow } from '@singletons/window';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible types of frame elements.
|
||||||
|
*/
|
||||||
|
type CoreFrameElement = (HTMLIFrameElement | HTMLFrameElement | HTMLObjectElement | HTMLEmbedElement) & {
|
||||||
|
window?: Window;
|
||||||
|
getWindow?(): Window;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Utils" service with helper functions for iframes, embed and similar.
|
* "Utils" service with helper functions for iframes, embed and similar.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CoreIframeUtilsProvider {
|
export class CoreIframeUtilsProvider {
|
||||||
static FRAME_TAGS = ['iframe', 'frame', 'object', 'embed'];
|
|
||||||
|
static readonly FRAME_TAGS = ['iframe', 'frame', 'object', 'embed'];
|
||||||
|
|
||||||
protected logger: CoreLogger;
|
protected logger: CoreLogger;
|
||||||
|
|
||||||
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));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +64,8 @@ export class CoreIframeUtilsProvider {
|
||||||
* @param isSubframe Whether it's a frame inside another frame.
|
* @param isSubframe Whether it's a frame inside another frame.
|
||||||
* @return True if frame is online and the app is offline, false otherwise.
|
* @return True if frame is online and the app is offline, false otherwise.
|
||||||
*/
|
*/
|
||||||
checkOnlineFrameInOffline(element: any, isSubframe?: boolean): boolean {
|
checkOnlineFrameInOffline(element: CoreFrameElement, isSubframe?: boolean): boolean {
|
||||||
const src = element.src || element.data;
|
const src = 'src' in element ? element.src : element.data;
|
||||||
|
|
||||||
if (src && src != 'about:blank' && !CoreUrlUtils.instance.isLocalFileUrl(src) && !CoreApp.instance.isOnline()) {
|
if (src && src != 'about:blank' && !CoreUrlUtils.instance.isLocalFileUrl(src) && !CoreApp.instance.isOnline()) {
|
||||||
if (element.classList.contains('core-iframe-offline-disabled')) {
|
if (element.classList.contains('core-iframe-offline-disabled')) {
|
||||||
|
@ -86,8 +80,6 @@ export class CoreIframeUtilsProvider {
|
||||||
div.setAttribute('padding', '');
|
div.setAttribute('padding', '');
|
||||||
div.classList.add('core-iframe-offline-warning');
|
div.classList.add('core-iframe-offline-warning');
|
||||||
|
|
||||||
const site = CoreSites.instance.getCurrentSite();
|
|
||||||
const username = site ? site.getInfo().username : undefined;
|
|
||||||
// @todo Handle link
|
// @todo Handle link
|
||||||
|
|
||||||
// Add a class to specify that the iframe is hidden.
|
// Add a class to specify that the iframe is hidden.
|
||||||
|
@ -112,8 +104,13 @@ export class CoreIframeUtilsProvider {
|
||||||
return true;
|
return true;
|
||||||
} else if (element.classList.contains('core-iframe-offline-disabled')) {
|
} else if (element.classList.contains('core-iframe-offline-disabled')) {
|
||||||
// Reload the frame.
|
// Reload the frame.
|
||||||
element.src = element.src;
|
if ('src' in element) {
|
||||||
element.data = element.data;
|
// eslint-disable-next-line no-self-assign
|
||||||
|
element.src = element.src;
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line no-self-assign
|
||||||
|
element.data = element.data;
|
||||||
|
}
|
||||||
|
|
||||||
// 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');
|
||||||
|
@ -134,12 +131,14 @@ 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: any): { window: Window, document: Document } {
|
getContentWindowAndDocument(element: CoreFrameElement): { window: Window; document: Document } {
|
||||||
let contentWindow: Window = element.contentWindow;
|
let contentWindow: Window = 'contentWindow' in element ? element.contentWindow : undefined;
|
||||||
let contentDocument: Document;
|
let contentDocument: Document;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
contentDocument = element.contentDocument || (contentWindow && contentWindow.document);
|
contentDocument = 'contentDocument' in element && element.contentDocument
|
||||||
|
? element.contentDocument
|
||||||
|
: contentWindow && contentWindow.document;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}
|
}
|
||||||
|
@ -149,7 +148,7 @@ export class CoreIframeUtilsProvider {
|
||||||
contentWindow = contentDocument.defaultView;
|
contentWindow = contentDocument.defaultView;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!contentWindow && element.getSVGDocument) {
|
if (!contentWindow && 'getSVGDocument' in element) {
|
||||||
// It's probably an <embed>. Try to get the window and the document.
|
// It's probably an <embed>. Try to get the window and the document.
|
||||||
try {
|
try {
|
||||||
contentDocument = element.getSVGDocument();
|
contentDocument = element.getSVGDocument();
|
||||||
|
@ -187,9 +186,6 @@ export class CoreIframeUtilsProvider {
|
||||||
case 'link_clicked':
|
case 'link_clicked':
|
||||||
this.linkClicked(event.data.link);
|
this.linkClicked(event.data.link);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,10 +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: any, contentWindow: Window, contentDocument: Document, navCtrl?: any): void {
|
redefineWindowOpen(
|
||||||
|
element: CoreFrameElement,
|
||||||
|
contentWindow: Window,
|
||||||
|
contentDocument: Document,
|
||||||
|
navCtrl?: NavController,
|
||||||
|
): void {
|
||||||
if (contentWindow) {
|
if (contentWindow) {
|
||||||
// Intercept window.open.
|
// Intercept window.open.
|
||||||
(<any> 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;
|
||||||
|
@ -216,7 +217,7 @@ export class CoreIframeUtilsProvider {
|
||||||
// Search sub frames.
|
// Search sub frames.
|
||||||
CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => {
|
CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => {
|
||||||
const elements = Array.from(contentDocument.querySelectorAll(tag));
|
const elements = Array.from(contentDocument.querySelectorAll(tag));
|
||||||
elements.forEach((subElement) => {
|
elements.forEach((subElement: CoreFrameElement) => {
|
||||||
this.treatFrame(subElement, true, navCtrl);
|
this.treatFrame(subElement, true, navCtrl);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -231,7 +232,7 @@ export class CoreIframeUtilsProvider {
|
||||||
* @param isSubframe Whether it's a frame inside another frame.
|
* @param isSubframe Whether it's a frame inside another frame.
|
||||||
* @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: any, isSubframe?: boolean, navCtrl?: any): void {
|
treatFrame(element: CoreFrameElement, isSubframe?: boolean, navCtrl?: NavController): void {
|
||||||
if (element) {
|
if (element) {
|
||||||
this.checkOnlineFrameInOffline(element, isSubframe);
|
this.checkOnlineFrameInOffline(element, isSubframe);
|
||||||
|
|
||||||
|
@ -266,7 +267,7 @@ export class CoreIframeUtilsProvider {
|
||||||
* @param element Element to treat (iframe, embed, ...).
|
* @param element Element to treat (iframe, embed, ...).
|
||||||
* @param contentDocument The document of the element contents.
|
* @param contentDocument The document of the element contents.
|
||||||
*/
|
*/
|
||||||
treatFrameLinks(element: any, contentDocument: Document): void {
|
treatFrameLinks(element: CoreFrameElement, contentDocument: Document): void {
|
||||||
if (!contentDocument) {
|
if (!contentDocument) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -292,7 +293,7 @@ export class CoreIframeUtilsProvider {
|
||||||
link.treated = true;
|
link.treated = true;
|
||||||
link.addEventListener('click', this.linkClicked.bind(this, link, element));
|
link.addEventListener('click', this.linkClicked.bind(this, link, element));
|
||||||
}, {
|
}, {
|
||||||
capture: true // Use capture to fix this listener not called if the element clicked is too deep in the DOM.
|
capture: true, // Use capture to fix this listener not called if the element clicked is too deep in the DOM.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,11 +306,13 @@ 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.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async windowOpen(url: string, name: string, element?: any, navCtrl?: any): Promise<void> {
|
protected async windowOpen(url: string, name: string, element?: CoreFrameElement, navCtrl?: NavController): Promise<void> {
|
||||||
const scheme = CoreUrlUtils.instance.getUrlScheme(url);
|
const scheme = CoreUrlUtils.instance.getUrlScheme(url);
|
||||||
if (!scheme) {
|
if (!scheme) {
|
||||||
// It's a relative URL, use the frame src to create the full URL.
|
// It's a relative URL, use the frame src to create the full URL.
|
||||||
const src = element && (element.src || element.data);
|
const src = element
|
||||||
|
? ('src' in element ? element.src : element.data)
|
||||||
|
: null;
|
||||||
if (src) {
|
if (src) {
|
||||||
const dirAndFile = CoreFile.instance.getFileAndDirectoryFromPath(src);
|
const dirAndFile = CoreFile.instance.getFileAndDirectoryFromPath(src);
|
||||||
if (dirAndFile.directory) {
|
if (dirAndFile.directory) {
|
||||||
|
@ -372,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;
|
||||||
|
@ -438,6 +444,28 @@ 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) {}
|
||||||
|
|
|
@ -13,36 +13,55 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { FileEntry } from '@ionic-native/file';
|
||||||
|
|
||||||
import { CoreFile } from '@services/file';
|
import { CoreFile } from '@services/file';
|
||||||
import { CoreTextUtils } from '@services/utils/text';
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
import { makeSingleton, Translate, Http } from '@singletons/core.singletons';
|
import { makeSingleton, Translate, Http } from '@singletons/core.singletons';
|
||||||
import { CoreLogger } from '@singletons/logger';
|
import { CoreLogger } from '@singletons/logger';
|
||||||
|
import { CoreWSExternalFile } from '@services/ws';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
|
||||||
|
interface MimeTypeInfo {
|
||||||
|
type: string;
|
||||||
|
icon?: string;
|
||||||
|
groups?: string[];
|
||||||
|
|
||||||
|
// eslint-disable-next-line id-blacklist
|
||||||
|
string?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MimeTypeGroupInfo {
|
||||||
|
mimetypes: string[];
|
||||||
|
extensions: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXTENSION_REGEX = /^[a-z0-9]+$/;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Utils" service with helper functions for mimetypes and extensions.
|
* "Utils" service with helper functions for mimetypes and extensions.
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CoreMimetypeUtilsProvider {
|
export class CoreMimetypeUtilsProvider {
|
||||||
|
|
||||||
protected logger: CoreLogger;
|
protected logger: CoreLogger;
|
||||||
protected extToMime = {}; // Object to map extensions -> mimetypes.
|
protected extToMime: Record<string, MimeTypeInfo> = {};
|
||||||
protected mimeToExt = {}; // Object to map mimetypes -> extensions.
|
protected mimeToExt: Record<string, string[]> = {};
|
||||||
protected groupsMimeInfo = {}; // Object to hold extensions and mimetypes that belong to a certain "group" (audio, video, ...).
|
protected groupsMimeInfo: Record<string, MimeTypeGroupInfo> = {};
|
||||||
protected extensionRegex = /^[a-z0-9]+$/;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.logger = CoreLogger.getInstance('CoreMimetypeUtilsProvider');
|
this.logger = CoreLogger.getInstance('CoreMimetypeUtilsProvider');
|
||||||
|
|
||||||
Http.instance.get('assets/exttomime.json').subscribe((result) => {
|
Http.instance.get('assets/exttomime.json').subscribe((result: Record<string, MimeTypeInfo>) => {
|
||||||
this.extToMime = result;
|
this.extToMime = result;
|
||||||
}, (err) => {
|
}, () => {
|
||||||
// Error, shouldn't happen.
|
// Error, shouldn't happen.
|
||||||
});
|
});
|
||||||
|
|
||||||
Http.instance.get('assets/mimetoext.json').subscribe((result) => {
|
Http.instance.get('assets/mimetoext.json').subscribe((result: Record<string, string[]>) => {
|
||||||
this.mimeToExt = result;
|
this.mimeToExt = result;
|
||||||
}, (err) => {
|
}, () => {
|
||||||
// Error, shouldn't happen.
|
// Error, shouldn't happen
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,31 +167,38 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* Set the embed type to display an embedded file and mimetype if not found.
|
* Set the embed type to display an embedded file and mimetype if not found.
|
||||||
*
|
*
|
||||||
* @param file File object.
|
* @param file File object.
|
||||||
* @paran path Alternative path that will override fileurl from file object.
|
* @param path Alternative path that will override fileurl from file object.
|
||||||
*/
|
*/
|
||||||
getEmbeddedHtml(file: any, path?: string): string {
|
getEmbeddedHtml(file: CoreWSExternalFile | FileEntry, path?: string): string {
|
||||||
let ext;
|
const filename = CoreUtils.instance.isFileEntry(file) ? (file as FileEntry).name : file.filename;
|
||||||
const filename = file.filename || file.name;
|
const extension = !CoreUtils.instance.isFileEntry(file) && file.mimetype
|
||||||
|
? this.getExtension(file.mimetype)
|
||||||
|
: this.getFileExtension(filename);
|
||||||
|
const mimeType = !CoreUtils.instance.isFileEntry(file) && file.mimetype ? file.mimetype : this.getMimeType(extension);
|
||||||
|
|
||||||
if (file.mimetype) {
|
// @todo linting: See if this can be removed
|
||||||
ext = this.getExtension(file.mimetype);
|
(file as CoreWSExternalFile).mimetype = mimeType;
|
||||||
} else {
|
|
||||||
ext = this.getFileExtension(filename);
|
|
||||||
file.mimetype = this.getMimeType(ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.canBeEmbedded(ext)) {
|
if (this.canBeEmbedded(extension)) {
|
||||||
file.embedType = this.getExtensionType(ext);
|
const embedType = this.getExtensionType(extension);
|
||||||
|
|
||||||
path = CoreFile.instance.convertFileSrc(path || file.fileurl || file.url || (file.toURL && file.toURL()));
|
// @todo linting: See if this can be removed
|
||||||
|
(file as { embedType: string }).embedType = embedType;
|
||||||
|
|
||||||
if (file.embedType == 'image') {
|
path = CoreFile.instance.convertFileSrc(path ?? (CoreUtils.instance.isFileEntry(file) ? file.toURL() : file.fileurl));
|
||||||
return '<img src="' + path + '">';
|
|
||||||
}
|
switch (embedType) {
|
||||||
if (file.embedType == 'audio' || file.embedType == 'video') {
|
case 'image':
|
||||||
return '<' + file.embedType + ' controls title="' + filename + '" src="' + path + '">' +
|
return `<img src="${path}">`;
|
||||||
'<source src="' + path + '" type="' + file.mimetype + '">' +
|
case 'audio':
|
||||||
'</' + file.embedType + '>';
|
case 'video':
|
||||||
|
return [
|
||||||
|
`<${embedType} controls title="${filename}" src="${path}">`,
|
||||||
|
`<source src="${path}" type="${mimeType}">`,
|
||||||
|
`</${embedType}>`,
|
||||||
|
].join('');
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,7 +316,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
candidate = candidate.substr(0, position);
|
candidate = candidate.substr(0, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.extensionRegex.test(candidate)) {
|
if (EXTENSION_REGEX.test(candidate)) {
|
||||||
extension = candidate;
|
extension = candidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,7 +364,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param field The field to get. If not supplied, all the info will be returned.
|
* @param field The field to get. If not supplied, all the info will be returned.
|
||||||
* @return Info for the group.
|
* @return Info for the group.
|
||||||
*/
|
*/
|
||||||
getGroupMimeInfo(group: string, field?: string): any {
|
getGroupMimeInfo(group: string, field?: string): MimeTypeGroupInfo {
|
||||||
if (typeof this.groupsMimeInfo[group] == 'undefined') {
|
if (typeof this.groupsMimeInfo[group] == 'undefined') {
|
||||||
this.fillGroupMimeInfo(group);
|
this.fillGroupMimeInfo(group);
|
||||||
}
|
}
|
||||||
|
@ -372,13 +398,13 @@ export class CoreMimetypeUtilsProvider {
|
||||||
* @param capitalise If true, capitalises first character of result.
|
* @param capitalise If true, capitalises first character of result.
|
||||||
* @return Type description.
|
* @return Type description.
|
||||||
*/
|
*/
|
||||||
getMimetypeDescription(obj: any, 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 = '';
|
||||||
let mimetype = '';
|
let mimetype = '';
|
||||||
let extension = '';
|
let extension = '';
|
||||||
|
|
||||||
if (typeof obj == 'object' && typeof obj.file == 'function') {
|
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.
|
||||||
filename = obj.name;
|
filename = obj.name;
|
||||||
} else if (typeof obj == 'object') {
|
} else if (typeof obj == 'object') {
|
||||||
|
@ -548,6 +574,7 @@ export class CoreMimetypeUtilsProvider {
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CoreMimetypeUtils extends makeSingleton(CoreMimetypeUtilsProvider) {}
|
export class CoreMimetypeUtils extends makeSingleton(CoreMimetypeUtilsProvider) {}
|
||||||
|
|
|
@ -18,6 +18,8 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||||
import { CoreApp } from '@services/app';
|
import { CoreApp } from '@services/app';
|
||||||
import { CoreLang } from '@services/lang';
|
import { CoreLang } from '@services/lang';
|
||||||
import { makeSingleton, Translate } from '@singletons/core.singletons';
|
import { makeSingleton, Translate } from '@singletons/core.singletons';
|
||||||
|
import { CoreWSExternalFile } from '@services/ws';
|
||||||
|
import { Locutus } from '@singletons/locutus';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Different type of errors the app can treat.
|
* Different type of errors the app can treat.
|
||||||
|
@ -36,53 +38,53 @@ export type CoreTextErrorObject = {
|
||||||
export class CoreTextUtilsProvider {
|
export class CoreTextUtilsProvider {
|
||||||
|
|
||||||
// List of regular expressions to convert the old nomenclature to new nomenclature for disabled features.
|
// List of regular expressions to convert the old nomenclature to new nomenclature for disabled features.
|
||||||
protected DISABLED_FEATURES_COMPAT_REGEXPS = [
|
protected readonly DISABLED_FEATURES_COMPAT_REGEXPS: { old: RegExp; new: string }[] = [
|
||||||
{old: /\$mmLoginEmailSignup/g, new: 'CoreLoginEmailSignup'},
|
{ old: /\$mmLoginEmailSignup/g, new: 'CoreLoginEmailSignup' },
|
||||||
{old: /\$mmSideMenuDelegate/g, new: 'CoreMainMenuDelegate'},
|
{ old: /\$mmSideMenuDelegate/g, new: 'CoreMainMenuDelegate' },
|
||||||
{old: /\$mmCoursesDelegate/g, new: 'CoreCourseOptionsDelegate'},
|
{ old: /\$mmCoursesDelegate/g, new: 'CoreCourseOptionsDelegate' },
|
||||||
{old: /\$mmUserDelegate/g, new: 'CoreUserDelegate'},
|
{ old: /\$mmUserDelegate/g, new: 'CoreUserDelegate' },
|
||||||
{old: /\$mmCourseDelegate/g, new: 'CoreCourseModuleDelegate'},
|
{ old: /\$mmCourseDelegate/g, new: 'CoreCourseModuleDelegate' },
|
||||||
{old: /_mmCourses/g, new: '_CoreCourses'},
|
{ old: /_mmCourses/g, new: '_CoreCourses' },
|
||||||
{old: /_mmaFrontpage/g, new: '_CoreSiteHome'},
|
{ old: /_mmaFrontpage/g, new: '_CoreSiteHome' },
|
||||||
{old: /_mmaGrades/g, new: '_CoreGrades'},
|
{ old: /_mmaGrades/g, new: '_CoreGrades' },
|
||||||
{old: /_mmaCompetency/g, new: '_AddonCompetency'},
|
{ old: /_mmaCompetency/g, new: '_AddonCompetency' },
|
||||||
{old: /_mmaNotifications/g, new: '_AddonNotifications'},
|
{ old: /_mmaNotifications/g, new: '_AddonNotifications' },
|
||||||
{old: /_mmaMessages/g, new: '_AddonMessages'},
|
{ old: /_mmaMessages/g, new: '_AddonMessages' },
|
||||||
{old: /_mmaCalendar/g, new: '_AddonCalendar'},
|
{ old: /_mmaCalendar/g, new: '_AddonCalendar' },
|
||||||
{old: /_mmaFiles/g, new: '_AddonFiles'},
|
{ old: /_mmaFiles/g, new: '_AddonFiles' },
|
||||||
{old: /_mmaParticipants/g, new: '_CoreUserParticipants'},
|
{ old: /_mmaParticipants/g, new: '_CoreUserParticipants' },
|
||||||
{old: /_mmaCourseCompletion/g, new: '_AddonCourseCompletion'},
|
{ old: /_mmaCourseCompletion/g, new: '_AddonCourseCompletion' },
|
||||||
{old: /_mmaNotes/g, new: '_AddonNotes'},
|
{ old: /_mmaNotes/g, new: '_AddonNotes' },
|
||||||
{old: /_mmaBadges/g, new: '_AddonBadges'},
|
{ old: /_mmaBadges/g, new: '_AddonBadges' },
|
||||||
{old: /files_privatefiles/g, new: 'AddonFilesPrivateFiles'},
|
{ old: /files_privatefiles/g, new: 'AddonFilesPrivateFiles' },
|
||||||
{old: /files_sitefiles/g, new: 'AddonFilesSiteFiles'},
|
{ old: /files_sitefiles/g, new: 'AddonFilesSiteFiles' },
|
||||||
{old: /files_upload/g, new: 'AddonFilesUpload'},
|
{ old: /files_upload/g, new: 'AddonFilesUpload' },
|
||||||
{old: /_mmaModAssign/g, new: '_AddonModAssign'},
|
{ old: /_mmaModAssign/g, new: '_AddonModAssign' },
|
||||||
{old: /_mmaModBook/g, new: '_AddonModBook'},
|
{ old: /_mmaModBook/g, new: '_AddonModBook' },
|
||||||
{old: /_mmaModChat/g, new: '_AddonModChat'},
|
{ old: /_mmaModChat/g, new: '_AddonModChat' },
|
||||||
{old: /_mmaModChoice/g, new: '_AddonModChoice'},
|
{ old: /_mmaModChoice/g, new: '_AddonModChoice' },
|
||||||
{old: /_mmaModData/g, new: '_AddonModData'},
|
{ old: /_mmaModData/g, new: '_AddonModData' },
|
||||||
{old: /_mmaModFeedback/g, new: '_AddonModFeedback'},
|
{ old: /_mmaModFeedback/g, new: '_AddonModFeedback' },
|
||||||
{old: /_mmaModFolder/g, new: '_AddonModFolder'},
|
{ old: /_mmaModFolder/g, new: '_AddonModFolder' },
|
||||||
{old: /_mmaModForum/g, new: '_AddonModForum'},
|
{ old: /_mmaModForum/g, new: '_AddonModForum' },
|
||||||
{old: /_mmaModGlossary/g, new: '_AddonModGlossary'},
|
{ old: /_mmaModGlossary/g, new: '_AddonModGlossary' },
|
||||||
{old: /_mmaModH5pactivity/g, new: '_AddonModH5PActivity'},
|
{ old: /_mmaModH5pactivity/g, new: '_AddonModH5PActivity' },
|
||||||
{old: /_mmaModImscp/g, new: '_AddonModImscp'},
|
{ old: /_mmaModImscp/g, new: '_AddonModImscp' },
|
||||||
{old: /_mmaModLabel/g, new: '_AddonModLabel'},
|
{ old: /_mmaModLabel/g, new: '_AddonModLabel' },
|
||||||
{old: /_mmaModLesson/g, new: '_AddonModLesson'},
|
{ old: /_mmaModLesson/g, new: '_AddonModLesson' },
|
||||||
{old: /_mmaModLti/g, new: '_AddonModLti'},
|
{ old: /_mmaModLti/g, new: '_AddonModLti' },
|
||||||
{old: /_mmaModPage/g, new: '_AddonModPage'},
|
{ old: /_mmaModPage/g, new: '_AddonModPage' },
|
||||||
{old: /_mmaModQuiz/g, new: '_AddonModQuiz'},
|
{ old: /_mmaModQuiz/g, new: '_AddonModQuiz' },
|
||||||
{old: /_mmaModResource/g, new: '_AddonModResource'},
|
{ old: /_mmaModResource/g, new: '_AddonModResource' },
|
||||||
{old: /_mmaModScorm/g, new: '_AddonModScorm'},
|
{ old: /_mmaModScorm/g, new: '_AddonModScorm' },
|
||||||
{old: /_mmaModSurvey/g, new: '_AddonModSurvey'},
|
{ old: /_mmaModSurvey/g, new: '_AddonModSurvey' },
|
||||||
{old: /_mmaModUrl/g, new: '_AddonModUrl'},
|
{ old: /_mmaModUrl/g, new: '_AddonModUrl' },
|
||||||
{old: /_mmaModWiki/g, new: '_AddonModWiki'},
|
{ old: /_mmaModWiki/g, new: '_AddonModWiki' },
|
||||||
{old: /_mmaModWorkshop/g, new: '_AddonModWorkshop'},
|
{ old: /_mmaModWorkshop/g, new: '_AddonModWorkshop' },
|
||||||
{old: /remoteAddOn_/g, new: 'sitePlugin_'},
|
{ old: /remoteAddOn_/g, new: 'sitePlugin_' },
|
||||||
];
|
];
|
||||||
|
|
||||||
protected template = document.createElement('template'); // A template element to convert HTML to element.
|
protected template: HTMLTemplateElement = document.createElement('template'); // A template element to convert HTML to element.
|
||||||
|
|
||||||
constructor(private sanitizer: DomSanitizer) { }
|
constructor(private sanitizer: DomSanitizer) { }
|
||||||
|
|
||||||
|
@ -200,7 +202,6 @@ export class CoreTextUtilsProvider {
|
||||||
* @return Size in human readable format.
|
* @return Size in human readable format.
|
||||||
*/
|
*/
|
||||||
bytesToSize(bytes: number, precision: number = 2): string {
|
bytesToSize(bytes: number, precision: number = 2): string {
|
||||||
|
|
||||||
if (typeof bytes == 'undefined' || bytes === null || bytes < 0) {
|
if (typeof bytes == 'undefined' || bytes === null || bytes < 0) {
|
||||||
return Translate.instance.instant('core.notapplicable');
|
return Translate.instance.instant('core.notapplicable');
|
||||||
}
|
}
|
||||||
|
@ -449,9 +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?: any[],
|
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,
|
||||||
|
@ -531,12 +540,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: any[]): string {
|
getTextPluginfileUrl(files: CoreWSExternalFile[]): string {
|
||||||
if (files && files.length) {
|
if (files && files.length) {
|
||||||
const fileURL = files[0].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 fileURL.substr(0, Math.max(fileURL.lastIndexOf('/'), fileURL.lastIndexOf('%2F')));
|
return url.substr(0, Math.max(url.lastIndexOf('/'), url.lastIndexOf('%2F')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -610,13 +619,17 @@ export class CoreTextUtilsProvider {
|
||||||
* @param data Object to be checked.
|
* @param data Object to be checked.
|
||||||
* @return If the data has any long Unicode char on it.
|
* @return If the data has any long Unicode char on it.
|
||||||
*/
|
*/
|
||||||
hasUnicodeData(data: object): boolean {
|
hasUnicodeData(data: Record<string, unknown>): boolean {
|
||||||
for (const el in data) {
|
for (const el in data) {
|
||||||
if (typeof data[el] == 'object') {
|
if (typeof data[el] == 'object') {
|
||||||
if (this.hasUnicodeData(data[el])) {
|
if (this.hasUnicodeData(data[el] as Record<string, unknown>)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (typeof data[el] == 'string' && this.hasUnicode(data[el])) {
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data[el] == 'string' && this.hasUnicode(data[el] as string)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -628,17 +641,17 @@ export class CoreTextUtilsProvider {
|
||||||
* Same as Javascript's JSON.parse, but it will handle errors.
|
* Same as Javascript's JSON.parse, but it will handle errors.
|
||||||
*
|
*
|
||||||
* @param json JSON text.
|
* @param json JSON text.
|
||||||
* @param defaultValue Default value t oreturn if the parse fails. Defaults to the original value.
|
* @param defaultValue Default value to return if the parse fails. Defaults to the original value.
|
||||||
* @param logErrorFn An error to call with the exception to log the error. If not supplied, no error.
|
* @param logErrorFn An error to call with the exception to log the error. If not supplied, no error.
|
||||||
* @return JSON parsed as object or what it gets.
|
* @return JSON parsed as object or what it gets.
|
||||||
*/
|
*/
|
||||||
parseJSON(json: string, defaultValue?: any, logErrorFn?: (error?: any) => void): any {
|
parseJSON<T>(json: string, defaultValue?: T, logErrorFn?: (error?: Error) => void): T | string {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(json);
|
return JSON.parse(json);
|
||||||
} catch (ex) {
|
} catch (error) {
|
||||||
// Error, log the error if needed.
|
// Error, log the error if needed.
|
||||||
if (logErrorFn) {
|
if (logErrorFn) {
|
||||||
logErrorFn(ex);
|
logErrorFn(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,7 +688,7 @@ export class CoreTextUtilsProvider {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return text.replace(/[#:\/\?\\]+/g, '_');
|
return text.replace(/[#:/?\\]+/g, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -700,7 +713,7 @@ export class CoreTextUtilsProvider {
|
||||||
* @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute.
|
* @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute.
|
||||||
* @return Treated text.
|
* @return Treated text.
|
||||||
*/
|
*/
|
||||||
replacePluginfileUrls(text: string, files: any[]): string {
|
replacePluginfileUrls(text: string, files: CoreWSExternalFile[]): string {
|
||||||
if (text && typeof text == 'string') {
|
if (text && typeof text == 'string') {
|
||||||
const fileURL = this.getTextPluginfileUrl(files);
|
const fileURL = this.getTextPluginfileUrl(files);
|
||||||
if (fileURL) {
|
if (fileURL) {
|
||||||
|
@ -718,7 +731,7 @@ export class CoreTextUtilsProvider {
|
||||||
* @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute.
|
* @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute.
|
||||||
* @return Treated text.
|
* @return Treated text.
|
||||||
*/
|
*/
|
||||||
restorePluginfileUrls(text: string, files: any[]): string {
|
restorePluginfileUrls(text: string, files: CoreWSExternalFile[]): string {
|
||||||
if (text && typeof text == 'string') {
|
if (text && typeof text == 'string') {
|
||||||
const fileURL = this.getTextPluginfileUrl(files);
|
const fileURL = this.getTextPluginfileUrl(files);
|
||||||
if (fileURL) {
|
if (fileURL) {
|
||||||
|
@ -804,7 +817,6 @@ export class CoreTextUtilsProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace text within a portion of a string. Equivalent to PHP's substr_replace.
|
* Replace text within a portion of a string. Equivalent to PHP's substr_replace.
|
||||||
* Credits to http://locutus.io/php/strings/substr_replace/
|
|
||||||
*
|
*
|
||||||
* @param str The string to treat.
|
* @param str The string to treat.
|
||||||
* @param replace The value to put inside the string.
|
* @param replace The value to put inside the string.
|
||||||
|
@ -814,22 +826,7 @@ export class CoreTextUtilsProvider {
|
||||||
* @return Treated string.
|
* @return Treated string.
|
||||||
*/
|
*/
|
||||||
substrReplace(str: string, replace: string, start: number, length?: number): string {
|
substrReplace(str: string, replace: string, start: number, length?: number): string {
|
||||||
length = typeof length != 'undefined' ? length : str.length;
|
return Locutus.substrReplace(str, replace, start, length);
|
||||||
|
|
||||||
if (start < 0) {
|
|
||||||
start = start + str.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length < 0) {
|
|
||||||
length = length + str.length - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
str.slice(0, start),
|
|
||||||
replace.substr(0, length),
|
|
||||||
replace.slice(length),
|
|
||||||
str.slice(start + length)
|
|
||||||
].join('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -867,14 +864,14 @@ export class CoreTextUtilsProvider {
|
||||||
return CoreLang.instance.getCurrentLanguage().then((language) => {
|
return CoreLang.instance.getCurrentLanguage().then((language) => {
|
||||||
// Match the current language.
|
// Match the current language.
|
||||||
const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g;
|
const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g;
|
||||||
let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g');
|
let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)</(?:lang|span)>', 'g');
|
||||||
|
|
||||||
if (!text.match(currentLangRegEx)) {
|
if (!text.match(currentLangRegEx)) {
|
||||||
// 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.
|
||||||
return text;
|
return text;
|
||||||
|
@ -915,221 +912,12 @@ export class CoreTextUtilsProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unserialize Array from PHP.
|
* Unserialize Array from PHP.
|
||||||
* Taken from: https://github.com/kvz/locutus/blob/master/src/php/var/unserialize.js
|
|
||||||
*
|
*
|
||||||
* @param data String to unserialize.
|
* @param data String to unserialize.
|
||||||
* @param logErrorFn An error to call with the exception to log the error. If not supplied, no error.
|
|
||||||
* @return Unserialized data.
|
* @return Unserialized data.
|
||||||
*/
|
*/
|
||||||
unserialize(data: string, logErrorFn?: (error?: string) => void): any {
|
unserialize<T = unknown>(data: string): T {
|
||||||
// Discuss at: http://locutus.io/php/unserialize/
|
return Locutus.unserialize<T>(data);
|
||||||
// Original by: Arpad Ray (mailto:arpad@php.net)
|
|
||||||
// Improved by: Pedro Tainha (http://www.pedrotainha.com)
|
|
||||||
// Improved by: Kevin van Zonneveld (http://kvz.io)
|
|
||||||
// Improved by: Kevin van Zonneveld (http://kvz.io)
|
|
||||||
// Improved by: Chris
|
|
||||||
// Improved by: James
|
|
||||||
// Improved by: Le Torbi
|
|
||||||
// Improved by: Eli Skeggs
|
|
||||||
// Bugfixed by: dptr1988
|
|
||||||
// Bugfixed by: Kevin van Zonneveld (http://kvz.io)
|
|
||||||
// Bugfixed by: Brett Zamir (http://brett-zamir.me)
|
|
||||||
// Bugfixed by: philippsimon (https://github.com/philippsimon/)
|
|
||||||
// Revised by: d3x
|
|
||||||
// Input by: Brett Zamir (http://brett-zamir.me)
|
|
||||||
// Input by: Martin (http://www.erlenwiese.de/)
|
|
||||||
// Input by: kilops
|
|
||||||
// Input by: Jaroslaw Czarniak
|
|
||||||
// Input by: lovasoa (https://github.com/lovasoa/)
|
|
||||||
// Note 1: We feel the main purpose of this function should be
|
|
||||||
// Note 1: to ease the transport of data between php & js
|
|
||||||
// Note 1: Aiming for PHP-compatibility, we have to translate objects to arrays
|
|
||||||
// Example 1: unserialize('a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}')
|
|
||||||
// Returns 1: ['Kevin', 'van', 'Zonneveld']
|
|
||||||
// Example 2: unserialize('a:2:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";}')
|
|
||||||
// Returns 2: {firstName: 'Kevin', midName: 'van'}
|
|
||||||
// Example 3: unserialize('a:3:{s:2:"ü";s:2:"ü";s:3:"四";s:3:"四";s:4:"𠜎";s:4:"𠜎";}')
|
|
||||||
// Returns 3: {'ü': 'ü', '四': '四', '𠜎': '𠜎'}
|
|
||||||
|
|
||||||
const utf8Overhead = (str: string): number => {
|
|
||||||
let s = str.length;
|
|
||||||
|
|
||||||
for (let i = str.length - 1; i >= 0; i--) {
|
|
||||||
const code = str.charCodeAt(i);
|
|
||||||
if (code > 0x7f && code <= 0x7ff) {
|
|
||||||
s++;
|
|
||||||
} else if (code > 0x7ff && code <= 0xffff) {
|
|
||||||
s += 2;
|
|
||||||
}
|
|
||||||
// Trail surrogate.
|
|
||||||
if (code >= 0xDC00 && code <= 0xDFFF) {
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s - 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const error = (type: string, msg: string): void => {
|
|
||||||
if (logErrorFn) {
|
|
||||||
logErrorFn(type + msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const readUntil = (data: string, offset: number, stopchr: string): Array<any> => {
|
|
||||||
let i = 2;
|
|
||||||
const buf = [];
|
|
||||||
let chr = data.slice(offset, offset + 1);
|
|
||||||
|
|
||||||
while (chr !== stopchr) {
|
|
||||||
if ((i + offset) > data.length) {
|
|
||||||
error('Error', 'Invalid');
|
|
||||||
}
|
|
||||||
buf.push(chr);
|
|
||||||
chr = data.slice(offset + (i - 1), offset + i);
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [buf.length, buf.join('')];
|
|
||||||
};
|
|
||||||
|
|
||||||
const readChrs = (data: string, offset: number, length: number): Array<any> => {
|
|
||||||
let chr;
|
|
||||||
const buf = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
chr = data.slice(offset + (i - 1), offset + i);
|
|
||||||
buf.push(chr);
|
|
||||||
length -= utf8Overhead(chr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [buf.length, buf.join('')];
|
|
||||||
};
|
|
||||||
|
|
||||||
const _unserialize = (data: string, offset: number): any => {
|
|
||||||
let dtype,
|
|
||||||
dataoffset,
|
|
||||||
keyandchrs,
|
|
||||||
keys,
|
|
||||||
contig,
|
|
||||||
length,
|
|
||||||
array,
|
|
||||||
readdata,
|
|
||||||
readData,
|
|
||||||
ccount,
|
|
||||||
stringlength,
|
|
||||||
i,
|
|
||||||
key,
|
|
||||||
kprops,
|
|
||||||
kchrs,
|
|
||||||
vprops,
|
|
||||||
vchrs,
|
|
||||||
value,
|
|
||||||
chrs = 0,
|
|
||||||
typeconvert = (x: any): any => {
|
|
||||||
return x;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!offset) {
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
dtype = (data.slice(offset, offset + 1)).toLowerCase();
|
|
||||||
|
|
||||||
dataoffset = offset + 2;
|
|
||||||
|
|
||||||
switch (dtype) {
|
|
||||||
case 'i':
|
|
||||||
typeconvert = (x: any): number => {
|
|
||||||
return parseInt(x, 10);
|
|
||||||
};
|
|
||||||
readData = readUntil(data, dataoffset, ';');
|
|
||||||
chrs = readData[0];
|
|
||||||
readdata = readData[1];
|
|
||||||
dataoffset += chrs + 1;
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
typeconvert = (x: any): boolean => {
|
|
||||||
return parseInt(x, 10) !== 0;
|
|
||||||
};
|
|
||||||
readData = readUntil(data, dataoffset, ';');
|
|
||||||
chrs = readData[0];
|
|
||||||
readdata = readData[1];
|
|
||||||
dataoffset += chrs + 1;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
typeconvert = (x: any): number => {
|
|
||||||
return parseFloat(x);
|
|
||||||
};
|
|
||||||
readData = readUntil(data, dataoffset, ';');
|
|
||||||
chrs = readData[0];
|
|
||||||
readdata = readData[1];
|
|
||||||
dataoffset += chrs + 1;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
readdata = null;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
ccount = readUntil(data, dataoffset, ':');
|
|
||||||
chrs = ccount[0];
|
|
||||||
stringlength = ccount[1];
|
|
||||||
dataoffset += chrs + 2;
|
|
||||||
|
|
||||||
readData = readChrs(data, dataoffset + 1, parseInt(stringlength, 10));
|
|
||||||
chrs = readData[0];
|
|
||||||
readdata = readData[1];
|
|
||||||
dataoffset += chrs + 2;
|
|
||||||
if (chrs !== parseInt(stringlength, 10) && chrs !== readdata.length) {
|
|
||||||
error('SyntaxError', 'String length mismatch');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
readdata = {};
|
|
||||||
|
|
||||||
keyandchrs = readUntil(data, dataoffset, ':');
|
|
||||||
chrs = keyandchrs[0];
|
|
||||||
keys = keyandchrs[1];
|
|
||||||
dataoffset += chrs + 2;
|
|
||||||
|
|
||||||
length = parseInt(keys, 10);
|
|
||||||
contig = true;
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
kprops = _unserialize(data, dataoffset);
|
|
||||||
kchrs = kprops[1];
|
|
||||||
key = kprops[2];
|
|
||||||
dataoffset += kchrs;
|
|
||||||
|
|
||||||
vprops = _unserialize(data, dataoffset);
|
|
||||||
vchrs = vprops[1];
|
|
||||||
value = vprops[2];
|
|
||||||
dataoffset += vchrs;
|
|
||||||
|
|
||||||
if (key !== i) {
|
|
||||||
contig = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
readdata[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contig) {
|
|
||||||
array = new Array(length);
|
|
||||||
for (i = 0; i < length; i++) {
|
|
||||||
array[i] = readdata[i];
|
|
||||||
}
|
|
||||||
readdata = array;
|
|
||||||
}
|
|
||||||
|
|
||||||
dataoffset += 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [dtype, dataoffset - offset, typeconvert(readdata)];
|
|
||||||
};
|
|
||||||
|
|
||||||
return _unserialize((data + ''), 0)[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1138,16 +926,13 @@ export class CoreTextUtilsProvider {
|
||||||
* @param title Title of the new state.
|
* @param title Title of the new state.
|
||||||
* @param text Content of the text to be expanded.
|
* @param text Content of the text to be expanded.
|
||||||
* @param component Component to link the embedded files to.
|
* @param component Component to link the embedded files to.
|
||||||
* @param componentId An ID to use in conjunction with the component.
|
* @param options Options.
|
||||||
* @param files List of files to display along with the text.
|
|
||||||
* @param filter Whether the text should be filtered.
|
|
||||||
* @param contextLevel The context level.
|
|
||||||
* @param instanceId The instance ID related to the context.
|
|
||||||
* @param courseId Course ID the text belongs to. It can be used to improve performance with filters.
|
|
||||||
*/
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
viewText(title: string, text: string, options?: CoreTextUtilsViewTextOptions): void {
|
viewText(title: string, text: string, options?: CoreTextUtilsViewTextOptions): void {
|
||||||
// @todo
|
// @todo
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1156,7 +941,7 @@ export class CoreTextUtilsProvider {
|
||||||
export type CoreTextUtilsViewTextOptions = {
|
export type CoreTextUtilsViewTextOptions = {
|
||||||
component?: string; // Component to link the embedded files to.
|
component?: string; // Component to link the embedded files to.
|
||||||
componentId?: string | number; // An ID to use in conjunction with the component.
|
componentId?: string | number; // An ID to use in conjunction with the component.
|
||||||
files?: any[]; // List of files to display along with the text.
|
files?: CoreWSExternalFile[]; // List of files to display along with the text.
|
||||||
filter?: boolean; // Whether the text should be filtered.
|
filter?: boolean; // Whether the text should be filtered.
|
||||||
contextLevel?: string; // The context level.
|
contextLevel?: string; // The context level.
|
||||||
instanceId?: number; // The instance ID related to the context.
|
instanceId?: number; // The instance ID related to the context.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,447 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Original code taken from https://github.com/kvz/locutus
|
||||||
|
*/
|
||||||
|
|
||||||
|
function initCache () {
|
||||||
|
const store = []
|
||||||
|
// cache only first element, second is length to jump ahead for the parser
|
||||||
|
const cache = function cache (value) {
|
||||||
|
store.push(value[0])
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.get = (index) => {
|
||||||
|
if (index >= store.length) {
|
||||||
|
throw RangeError(`Can't resolve reference ${index + 1}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return store[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectType (str, cache) {
|
||||||
|
const types = /^(?:N(?=;)|[bidsSaOCrR](?=:)|[^:]+(?=:))/g
|
||||||
|
const type = (types.exec(str) || [])[0]
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
throw SyntaxError('Invalid input: ' + str)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'N':
|
||||||
|
return cache([ null, 2 ])
|
||||||
|
case 'b':
|
||||||
|
return cache(expectBool(str))
|
||||||
|
case 'i':
|
||||||
|
return cache(expectInt(str))
|
||||||
|
case 'd':
|
||||||
|
return cache(expectFloat(str))
|
||||||
|
case 's':
|
||||||
|
return cache(expectString(str))
|
||||||
|
case 'S':
|
||||||
|
return cache(expectEscapedString(str))
|
||||||
|
case 'a':
|
||||||
|
return expectArray(str, cache)
|
||||||
|
case 'O':
|
||||||
|
return expectObject(str, cache)
|
||||||
|
case 'C':
|
||||||
|
return expectClass(str, cache)
|
||||||
|
case 'r':
|
||||||
|
case 'R':
|
||||||
|
return expectReference(str, cache)
|
||||||
|
default:
|
||||||
|
throw SyntaxError(`Invalid or unsupported data type: ${type}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectBool (str) {
|
||||||
|
const reBool = /^b:([01]);/
|
||||||
|
const [ match, boolMatch ] = reBool.exec(str) || []
|
||||||
|
|
||||||
|
if (!boolMatch) {
|
||||||
|
throw SyntaxError('Invalid bool value, expected 0 or 1')
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ boolMatch === '1', match.length ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectInt (str) {
|
||||||
|
const reInt = /^i:([+-]?\d+);/
|
||||||
|
const [ match, intMatch ] = reInt.exec(str) || []
|
||||||
|
|
||||||
|
if (!intMatch) {
|
||||||
|
throw SyntaxError('Expected an integer value')
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ parseInt(intMatch, 10), match.length ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectFloat (str) {
|
||||||
|
const reFloat = /^d:(NAN|-?INF|(?:\d+\.\d*|\d*\.\d+|\d+)(?:[eE][+-]\d+)?);/
|
||||||
|
const [ match, floatMatch ] = reFloat.exec(str) || []
|
||||||
|
|
||||||
|
if (!floatMatch) {
|
||||||
|
throw SyntaxError('Expected a float value')
|
||||||
|
}
|
||||||
|
|
||||||
|
let floatValue
|
||||||
|
|
||||||
|
switch (floatMatch) {
|
||||||
|
case 'NAN':
|
||||||
|
floatValue = Number.NaN
|
||||||
|
break
|
||||||
|
case '-INF':
|
||||||
|
floatValue = Number.NEGATIVE_INFINITY
|
||||||
|
break
|
||||||
|
case 'INF':
|
||||||
|
floatValue = Number.POSITIVE_INFINITY
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
floatValue = parseFloat(floatMatch)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ floatValue, match.length ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBytes (str, len, escapedString = false) {
|
||||||
|
let bytes = 0
|
||||||
|
let out = ''
|
||||||
|
let c = 0
|
||||||
|
const strLen = str.length
|
||||||
|
let wasHighSurrogate = false
|
||||||
|
let escapedChars = 0
|
||||||
|
|
||||||
|
while (bytes < len && c < strLen) {
|
||||||
|
let chr = str.charAt(c)
|
||||||
|
const code = chr.charCodeAt(0)
|
||||||
|
const isHighSurrogate = code >= 0xd800 && code <= 0xdbff
|
||||||
|
const isLowSurrogate = code >= 0xdc00 && code <= 0xdfff
|
||||||
|
|
||||||
|
if (escapedString && chr === '\\') {
|
||||||
|
chr = String.fromCharCode(parseInt(str.substr(c + 1, 2), 16))
|
||||||
|
escapedChars++
|
||||||
|
|
||||||
|
// each escaped sequence is 3 characters. Go 2 chars ahead.
|
||||||
|
// third character will be jumped over a few lines later
|
||||||
|
c += 2
|
||||||
|
}
|
||||||
|
|
||||||
|
c++
|
||||||
|
|
||||||
|
bytes += isHighSurrogate || (isLowSurrogate && wasHighSurrogate)
|
||||||
|
// if high surrogate, count 2 bytes, as expectation is to be followed by low surrogate
|
||||||
|
// if low surrogate preceded by high surrogate, add 2 bytes
|
||||||
|
? 2
|
||||||
|
: code > 0x7ff
|
||||||
|
// otherwise low surrogate falls into this part
|
||||||
|
? 3
|
||||||
|
: code > 0x7f
|
||||||
|
? 2
|
||||||
|
: 1
|
||||||
|
|
||||||
|
// if high surrogate is not followed by low surrogate, add 1 more byte
|
||||||
|
bytes += wasHighSurrogate && !isLowSurrogate ? 1 : 0
|
||||||
|
|
||||||
|
out += chr
|
||||||
|
wasHighSurrogate = isHighSurrogate
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ out, bytes, escapedChars ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectString (str) {
|
||||||
|
// PHP strings consist of one-byte characters.
|
||||||
|
// JS uses 2 bytes with possible surrogate pairs.
|
||||||
|
// Serialized length of 2 is still 1 JS string character
|
||||||
|
const reStrLength = /^s:(\d+):"/g // also match the opening " char
|
||||||
|
const [ match, byteLenMatch ] = reStrLength.exec(str) || []
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
throw SyntaxError('Expected a string value')
|
||||||
|
}
|
||||||
|
|
||||||
|
const len = parseInt(byteLenMatch, 10)
|
||||||
|
|
||||||
|
str = str.substr(match.length)
|
||||||
|
|
||||||
|
let [ strMatch, bytes ] = readBytes(str, len)
|
||||||
|
|
||||||
|
if (bytes !== len) {
|
||||||
|
throw SyntaxError(`Expected string of ${len} bytes, but got ${bytes}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.substr((strMatch as string).length)
|
||||||
|
|
||||||
|
// strict parsing, match closing "; chars
|
||||||
|
if (!str.startsWith('";')) {
|
||||||
|
throw SyntaxError('Expected ";')
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ strMatch, match.length + (strMatch as string).length + 2 ] // skip last ";
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectEscapedString (str) {
|
||||||
|
const reStrLength = /^S:(\d+):"/g // also match the opening " char
|
||||||
|
const [ match, strLenMatch ] = reStrLength.exec(str) || []
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
throw SyntaxError('Expected an escaped string value')
|
||||||
|
}
|
||||||
|
|
||||||
|
const len = parseInt(strLenMatch, 10)
|
||||||
|
|
||||||
|
str = str.substr(match.length)
|
||||||
|
|
||||||
|
let [ strMatch, bytes, escapedChars ] = readBytes(str, len, true)
|
||||||
|
|
||||||
|
if (bytes !== len) {
|
||||||
|
throw SyntaxError(`Expected escaped string of ${len} bytes, but got ${bytes}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.substr((strMatch as string).length + (escapedChars as number) * 2)
|
||||||
|
|
||||||
|
// strict parsing, match closing "; chars
|
||||||
|
if (!str.startsWith('";')) {
|
||||||
|
throw SyntaxError('Expected ";')
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ strMatch, match.length + (strMatch as string).length + 2 ] // skip last ";
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectKeyOrIndex (str) {
|
||||||
|
try {
|
||||||
|
return expectString(str)
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return expectEscapedString(str)
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return expectInt(str)
|
||||||
|
} catch (err) {
|
||||||
|
throw SyntaxError('Expected key or index')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectObject (str, cache) {
|
||||||
|
// O:<class name length>:"class name":<prop count>:{<props and values>}
|
||||||
|
// O:8:"stdClass":2:{s:3:"foo";s:3:"bar";s:3:"bar";s:3:"baz";}
|
||||||
|
const reObjectLiteral = /^O:(\d+):"([^"]+)":(\d+):\{/
|
||||||
|
const [ objectLiteralBeginMatch, /* classNameLengthMatch */, className, propCountMatch ] = reObjectLiteral.exec(str) || []
|
||||||
|
|
||||||
|
if (!objectLiteralBeginMatch) {
|
||||||
|
throw SyntaxError('Invalid input')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (className !== 'stdClass') {
|
||||||
|
throw SyntaxError(`Unsupported object type: ${className}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalOffset = objectLiteralBeginMatch.length
|
||||||
|
|
||||||
|
const propCount = parseInt(propCountMatch, 10)
|
||||||
|
const obj = {}
|
||||||
|
cache([obj])
|
||||||
|
|
||||||
|
str = str.substr(totalOffset)
|
||||||
|
|
||||||
|
for (let i = 0; i < propCount; i++) {
|
||||||
|
const prop = expectKeyOrIndex(str)
|
||||||
|
str = str.substr(prop[1])
|
||||||
|
totalOffset += prop[1] as number
|
||||||
|
|
||||||
|
const value = expectType(str, cache)
|
||||||
|
str = str.substr(value[1])
|
||||||
|
totalOffset += value[1]
|
||||||
|
|
||||||
|
obj[prop[0]] = value[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// strict parsing, expect } after object literal
|
||||||
|
if (str.charAt(0) !== '}') {
|
||||||
|
throw SyntaxError('Expected }')
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ obj, totalOffset + 1 ] // skip final }
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectClass (str, cache) {
|
||||||
|
// can't be well supported, because requires calling eval (or similar)
|
||||||
|
// in order to call serialized constructor name
|
||||||
|
// which is unsafe
|
||||||
|
// or assume that constructor is defined in global scope
|
||||||
|
// but this is too much limiting
|
||||||
|
throw Error('Not yet implemented')
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectReference (str, cache) {
|
||||||
|
const reRef = /^[rR]:([1-9]\d*);/
|
||||||
|
const [ match, refIndex ] = reRef.exec(str) || []
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
throw SyntaxError('Expected reference value')
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ cache.get(parseInt(refIndex, 10) - 1), match.length ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectArray (str, cache) {
|
||||||
|
const reArrayLength = /^a:(\d+):{/
|
||||||
|
const [ arrayLiteralBeginMatch, arrayLengthMatch ] = reArrayLength.exec(str) || []
|
||||||
|
|
||||||
|
if (!arrayLengthMatch) {
|
||||||
|
throw SyntaxError('Expected array length annotation')
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.substr(arrayLiteralBeginMatch.length)
|
||||||
|
|
||||||
|
const array = expectArrayItems(str, parseInt(arrayLengthMatch, 10), cache)
|
||||||
|
|
||||||
|
// strict parsing, expect closing } brace after array literal
|
||||||
|
if (str.charAt(array[1]) !== '}') {
|
||||||
|
throw SyntaxError('Expected }')
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ array[0], arrayLiteralBeginMatch.length + (array[1] as number) + 1 ] // jump over }
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectArrayItems (str, expectedItems = 0, cache) {
|
||||||
|
let key
|
||||||
|
let hasStringKeys = false
|
||||||
|
let item
|
||||||
|
let totalOffset = 0
|
||||||
|
let items = []
|
||||||
|
cache([items])
|
||||||
|
|
||||||
|
for (let i = 0; i < expectedItems; i++) {
|
||||||
|
key = expectKeyOrIndex(str)
|
||||||
|
|
||||||
|
// this is for backward compatibility with previous implementation
|
||||||
|
if (!hasStringKeys) {
|
||||||
|
hasStringKeys = (typeof key[0] === 'string')
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.substr(key[1])
|
||||||
|
totalOffset += key[1]
|
||||||
|
|
||||||
|
// references are resolved immediately, so if duplicate key overwrites previous array index
|
||||||
|
// the old value is anyway resolved
|
||||||
|
// fixme: but next time the same reference should point to the new value
|
||||||
|
item = expectType(str, cache)
|
||||||
|
str = str.substr(item[1])
|
||||||
|
totalOffset += item[1]
|
||||||
|
|
||||||
|
items[key[0]] = item[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is for backward compatibility with previous implementation
|
||||||
|
if (hasStringKeys) {
|
||||||
|
items = Object.assign({}, items)
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ items, totalOffset ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function unserialize (str) {
|
||||||
|
// discuss at: https://locutus.io/php/unserialize/
|
||||||
|
// original by: Arpad Ray (mailto:arpad@php.net)
|
||||||
|
// improved by: Pedro Tainha (https://www.pedrotainha.com)
|
||||||
|
// improved by: Kevin van Zonneveld (https://kvz.io)
|
||||||
|
// improved by: Kevin van Zonneveld (https://kvz.io)
|
||||||
|
// improved by: Chris
|
||||||
|
// improved by: James
|
||||||
|
// improved by: Le Torbi
|
||||||
|
// improved by: Eli Skeggs
|
||||||
|
// bugfixed by: dptr1988
|
||||||
|
// bugfixed by: Kevin van Zonneveld (https://kvz.io)
|
||||||
|
// bugfixed by: Brett Zamir (https://brett-zamir.me)
|
||||||
|
// bugfixed by: philippsimon (https://github.com/philippsimon/)
|
||||||
|
// revised by: d3x
|
||||||
|
// input by: Brett Zamir (https://brett-zamir.me)
|
||||||
|
// input by: Martin (https://www.erlenwiese.de/)
|
||||||
|
// input by: kilops
|
||||||
|
// input by: Jaroslaw Czarniak
|
||||||
|
// input by: lovasoa (https://github.com/lovasoa/)
|
||||||
|
// improved by: Rafał Kukawski
|
||||||
|
// reimplemented by: Rafał Kukawski
|
||||||
|
// note 1: We feel the main purpose of this function should be
|
||||||
|
// note 1: to ease the transport of data between php & js
|
||||||
|
// note 1: Aiming for PHP-compatibility, we have to translate objects to arrays
|
||||||
|
// example 1: unserialize('a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}')
|
||||||
|
// returns 1: ['Kevin', 'van', 'Zonneveld']
|
||||||
|
// example 2: unserialize('a:2:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";}')
|
||||||
|
// returns 2: {firstName: 'Kevin', midName: 'van'}
|
||||||
|
// example 3: unserialize('a:3:{s:2:"ü";s:2:"ü";s:3:"四";s:3:"四";s:4:"𠜎";s:4:"𠜎";}')
|
||||||
|
// returns 3: {'ü': 'ü', '四': '四', '𠜎': '𠜎'}
|
||||||
|
// example 4: unserialize(undefined)
|
||||||
|
// returns 4: false
|
||||||
|
// example 5: unserialize('O:8:"stdClass":1:{s:3:"foo";b:1;}')
|
||||||
|
// returns 5: { foo: true }
|
||||||
|
// example 6: unserialize('a:2:{i:0;N;i:1;s:0:"";}')
|
||||||
|
// returns 6: [null, ""]
|
||||||
|
// example 7: unserialize('S:7:"\\65\\73\\63\\61\\70\\65\\64";')
|
||||||
|
// returns 7: 'escaped'
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return expectType(str, initCache())[0]
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function substr_replace (str, replace, start, length) { // eslint-disable-line camelcase
|
||||||
|
// discuss at: https://locutus.io/php/substr_replace/
|
||||||
|
// original by: Brett Zamir (https://brett-zamir.me)
|
||||||
|
// example 1: substr_replace('ABCDEFGH:/MNRPQR/', 'bob', 0)
|
||||||
|
// returns 1: 'bob'
|
||||||
|
// example 2: var $var = 'ABCDEFGH:/MNRPQR/'
|
||||||
|
// example 2: substr_replace($var, 'bob', 0, $var.length)
|
||||||
|
// returns 2: 'bob'
|
||||||
|
// example 3: substr_replace('ABCDEFGH:/MNRPQR/', 'bob', 0, 0)
|
||||||
|
// returns 3: 'bobABCDEFGH:/MNRPQR/'
|
||||||
|
// example 4: substr_replace('ABCDEFGH:/MNRPQR/', 'bob', 10, -1)
|
||||||
|
// returns 4: 'ABCDEFGH:/bob/'
|
||||||
|
// example 5: substr_replace('ABCDEFGH:/MNRPQR/', 'bob', -7, -1)
|
||||||
|
// returns 5: 'ABCDEFGH:/bob/'
|
||||||
|
// example 6: substr_replace('ABCDEFGH:/MNRPQR/', '', 10, -1)
|
||||||
|
// returns 6: 'ABCDEFGH://'
|
||||||
|
|
||||||
|
if (start < 0) {
|
||||||
|
// start position in str
|
||||||
|
start = start + str.length
|
||||||
|
}
|
||||||
|
length = length !== undefined ? length : str.length
|
||||||
|
if (length < 0) {
|
||||||
|
length = length + str.length - start
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
str.slice(0, start),
|
||||||
|
replace.substr(0, length),
|
||||||
|
replace.slice(length),
|
||||||
|
str.slice(start + length)
|
||||||
|
].join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Locutus {
|
||||||
|
|
||||||
|
static unserialize<T = unknown>(data: string): T {
|
||||||
|
return unserialize(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static substrReplace(str: string, replace: string, start: number, length?: number): string {
|
||||||
|
return substr_replace(str, replace, start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -3,8 +3,9 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out-tsc/app",
|
"outDir": "./out-tsc/app",
|
||||||
"types": [
|
"types": [
|
||||||
"cordova",
|
|
||||||
"cordova-plugin-file-transfer",
|
"cordova-plugin-file-transfer",
|
||||||
|
"cordova-plugin-inappbrowser",
|
||||||
|
"cordova",
|
||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
"dom"
|
"dom"
|
||||||
],
|
],
|
||||||
"types": [
|
"types": [
|
||||||
|
"cordova-plugin-file-transfer",
|
||||||
|
"cordova-plugin-inappbrowser",
|
||||||
|
"cordova",
|
||||||
"jest",
|
"jest",
|
||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"outDir": "./out-tsc/tests",
|
"outDir": "./out-tsc/tests",
|
||||||
"types": [
|
"types": [
|
||||||
|
"cordova-plugin-file-transfer",
|
||||||
|
"cordova-plugin-inappbrowser",
|
||||||
|
"cordova",
|
||||||
"jest",
|
"jest",
|
||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
|
|
144
tslint.json
144
tslint.json
|
@ -1,144 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "tslint:recommended",
|
|
||||||
"rules": {
|
|
||||||
"array-type": false,
|
|
||||||
"arrow-return-shorthand": true,
|
|
||||||
"curly": true,
|
|
||||||
"deprecation": {
|
|
||||||
"severity": "warning"
|
|
||||||
},
|
|
||||||
"component-class-suffix": [true, "Page", "Component"],
|
|
||||||
"contextual-lifecycle": true,
|
|
||||||
"directive-class-suffix": true,
|
|
||||||
"directive-selector": [
|
|
||||||
true,
|
|
||||||
"attribute",
|
|
||||||
"app",
|
|
||||||
"camelCase"
|
|
||||||
],
|
|
||||||
"eofline": true,
|
|
||||||
"import-blacklist": [
|
|
||||||
true,
|
|
||||||
"rxjs/Rx"
|
|
||||||
],
|
|
||||||
"import-spacing": true,
|
|
||||||
"indent": {
|
|
||||||
"options": [
|
|
||||||
"spaces"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"max-classes-per-file": false,
|
|
||||||
"max-line-length": [
|
|
||||||
true,
|
|
||||||
140
|
|
||||||
],
|
|
||||||
"member-ordering": [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
"order": [
|
|
||||||
"static-field",
|
|
||||||
"instance-field",
|
|
||||||
"static-method",
|
|
||||||
"instance-method"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-angle-bracket-type-assertion": false,
|
|
||||||
"no-console": [
|
|
||||||
true,
|
|
||||||
"debug",
|
|
||||||
"info",
|
|
||||||
"time",
|
|
||||||
"timeEnd",
|
|
||||||
"trace"
|
|
||||||
],
|
|
||||||
"no-empty": false,
|
|
||||||
"no-inferrable-types": [
|
|
||||||
true,
|
|
||||||
"ignore-params"
|
|
||||||
],
|
|
||||||
"no-non-null-assertion": true,
|
|
||||||
"no-redundant-jsdoc": true,
|
|
||||||
"no-shadowed-variable": false,
|
|
||||||
"no-switch-case-fall-through": true,
|
|
||||||
"no-unused-expression": false,
|
|
||||||
"no-var-requires": false,
|
|
||||||
"object-literal-key-quotes": [
|
|
||||||
true,
|
|
||||||
"as-needed"
|
|
||||||
],
|
|
||||||
"prefer-for-of": false,
|
|
||||||
"quotemark": [
|
|
||||||
true,
|
|
||||||
"single"
|
|
||||||
],
|
|
||||||
"semicolon": {
|
|
||||||
"options": [
|
|
||||||
"always"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"space-before-function-paren": {
|
|
||||||
"options": {
|
|
||||||
"anonymous": "never",
|
|
||||||
"asyncArrow": "always",
|
|
||||||
"constructor": "never",
|
|
||||||
"method": "never",
|
|
||||||
"named": "never"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"typedef-whitespace": {
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"call-signature": "nospace",
|
|
||||||
"index-signature": "nospace",
|
|
||||||
"parameter": "nospace",
|
|
||||||
"property-declaration": "nospace",
|
|
||||||
"variable-declaration": "nospace"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"call-signature": "onespace",
|
|
||||||
"index-signature": "onespace",
|
|
||||||
"parameter": "onespace",
|
|
||||||
"property-declaration": "onespace",
|
|
||||||
"variable-declaration": "onespace"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"variable-name": {
|
|
||||||
"options": [
|
|
||||||
"ban-keywords",
|
|
||||||
"check-format",
|
|
||||||
"allow-pascal-case",
|
|
||||||
"allow-leading-underscore"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"whitespace": {
|
|
||||||
"options": [
|
|
||||||
"check-branch",
|
|
||||||
"check-decl",
|
|
||||||
"check-operator",
|
|
||||||
"check-separator",
|
|
||||||
"check-type",
|
|
||||||
"check-typecast"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"no-conflicting-lifecycle": true,
|
|
||||||
"no-host-metadata-property": true,
|
|
||||||
"no-input-rename": true,
|
|
||||||
"no-inputs-metadata-property": true,
|
|
||||||
"no-output-native": true,
|
|
||||||
"no-output-on-prefix": true,
|
|
||||||
"no-output-rename": true,
|
|
||||||
"no-outputs-metadata-property": true,
|
|
||||||
"template-banana-in-box": true,
|
|
||||||
"template-no-negated-async": true,
|
|
||||||
"use-lifecycle-interface": true,
|
|
||||||
"use-pipe-transform-interface": true,
|
|
||||||
"object-literal-sort-keys": false,
|
|
||||||
"forin": false,
|
|
||||||
"triple-equals": false
|
|
||||||
},
|
|
||||||
"rulesDirectory": [
|
|
||||||
"codelyzer"
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Reference in New Issue