forked from CIT/Vmeda.Online
		
	Merge pull request #2557 from NoelDeMartin/MOBILE-3320
MOBILE-3320 Updated linting rules + add Error tests
This commit is contained in:
		
						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 = { | ||||
|     root: true, | ||||
|     overrides: [ | ||||
|         { | ||||
|             files: ['*.ts'], | ||||
|             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', | ||||
|             }, | ||||
|         }, | ||||
|         Object.assign({ files: ['*.ts'] }, appConfig), | ||||
|         Object.assign({ files: ['*.test.ts'] }, testsConfig), | ||||
|         { | ||||
|             files: ['*.html'], | ||||
|             extends: ['plugin:@angular-eslint/template/recommended'], | ||||
|  | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -22,7 +22,6 @@ npm-debug.log* | ||||
| /.sass-cache | ||||
| /.sourcemaps | ||||
| /.versions | ||||
| /.vscode | ||||
| /coverage | ||||
| /dist | ||||
| /node_modules | ||||
|  | ||||
							
								
								
									
										35
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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", | ||||
|             } | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										145
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										145
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -3849,12 +3849,6 @@ | ||||
|         "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": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", | ||||
| @ -3876,16 +3870,6 @@ | ||||
|         "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": { | ||||
|       "version": "1.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", | ||||
| @ -4059,12 +4043,6 @@ | ||||
|       "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", | ||||
|       "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": { | ||||
|       "version": "1.0.0", | ||||
|       "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", | ||||
|       "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": { | ||||
|       "version": "26.5.2", | ||||
|       "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": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", | ||||
| @ -6559,16 +6474,6 @@ | ||||
|       "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", | ||||
|       "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": { | ||||
|       "version": "1.0.0-alpha.37", | ||||
|       "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==", | ||||
|       "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": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", | ||||
| @ -6765,12 +6661,6 @@ | ||||
|         "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": { | ||||
|       "version": "1.14.1", | ||||
|       "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", | ||||
| @ -7936,6 +7826,12 @@ | ||||
|       "integrity": "sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ==", | ||||
|       "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": { | ||||
|       "version": "4.0.3", | ||||
|       "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", | ||||
|       "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": { | ||||
|       "version": "3.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", | ||||
| @ -8363,12 +8265,6 @@ | ||||
|       "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", | ||||
|       "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": { | ||||
|       "version": "1.8.0", | ||||
|       "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": { | ||||
|       "version": "1.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", | ||||
|  | ||||
| @ -121,7 +121,6 @@ | ||||
|     "@angular/language-service": "~10.0.0", | ||||
|     "@ionic/angular-toolkit": "^2.3.0", | ||||
|     "@types/node": "^12.12.64", | ||||
|     "codelyzer": "^6.0.0", | ||||
|     "@typescript-eslint/eslint-plugin": "4.3.0", | ||||
|     "@typescript-eslint/parser": "4.3.0", | ||||
|     "eslint": "^7.6.0", | ||||
| @ -131,6 +130,8 @@ | ||||
|     "eslint-plugin-jest": "^24.1.0", | ||||
|     "eslint-plugin-jsdoc": "^30.6.3", | ||||
|     "eslint-plugin-prefer-arrow": "^1.2.2", | ||||
|     "eslint-plugin-promise": "^4.2.1", | ||||
|     "faker": "^5.1.0", | ||||
|     "jest": "^26.5.0", | ||||
|     "jest-preset-angular": "^8.3.1", | ||||
|     "ts-jest": "^26.4.1", | ||||
|  | ||||
							
								
								
									
										75
									
								
								src/app/classes/error.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/app/classes/error.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { NavController } from '@ionic/angular'; | ||||
| import { WKUserScriptWindow, WKUserScriptInjectionTime } from 'cordova-plugin-wkuserscript'; | ||||
| 
 | ||||
| import { CoreApp, CoreAppProvider } from '@services/app'; | ||||
| import { CoreApp } from '@services/app'; | ||||
| import { CoreFile } from '@services/file'; | ||||
| import { CoreFileHelper } from '@services/file-helper'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| @ -24,42 +25,35 @@ import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreUrlUtils } from '@services/utils/url'; | ||||
| 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 { CoreUrl } from '@singletons/url'; | ||||
| 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. | ||||
|  */ | ||||
| @Injectable() | ||||
| export class CoreIframeUtilsProvider { | ||||
|     static FRAME_TAGS = ['iframe', 'frame', 'object', 'embed']; | ||||
| 
 | ||||
|     static readonly FRAME_TAGS = ['iframe', 'frame', 'object', 'embed']; | ||||
| 
 | ||||
|     protected logger: CoreLogger; | ||||
| 
 | ||||
|     constructor() { | ||||
|         this.logger = CoreLogger.getInstance('CoreUtilsProvider'); | ||||
| 
 | ||||
|         const win = <WKUserScriptWindow> window; | ||||
| 
 | ||||
|         if (CoreApp.instance.isIOS() && win.WKUserScript) { | ||||
|             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)); | ||||
|             }); | ||||
|         if (CoreApp.instance.isIOS() && 'WKUserScript' in window) { | ||||
|             // eslint-disable-next-line promise/catch-or-return
 | ||||
|             Platform.instance.ready().then(() => this.injectiOSScripts(window)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -70,8 +64,8 @@ export class CoreIframeUtilsProvider { | ||||
|      * @param isSubframe Whether it's a frame inside another frame. | ||||
|      * @return True if frame is online and the app is offline, false otherwise. | ||||
|      */ | ||||
|     checkOnlineFrameInOffline(element: any, isSubframe?: boolean): boolean { | ||||
|         const src = element.src || element.data; | ||||
|     checkOnlineFrameInOffline(element: CoreFrameElement, isSubframe?: boolean): boolean { | ||||
|         const src = 'src' in element ? element.src : element.data; | ||||
| 
 | ||||
|         if (src && src != 'about:blank' && !CoreUrlUtils.instance.isLocalFileUrl(src) && !CoreApp.instance.isOnline()) { | ||||
|             if (element.classList.contains('core-iframe-offline-disabled')) { | ||||
| @ -86,8 +80,6 @@ export class CoreIframeUtilsProvider { | ||||
|             div.setAttribute('padding', ''); | ||||
|             div.classList.add('core-iframe-offline-warning'); | ||||
| 
 | ||||
|             const site = CoreSites.instance.getCurrentSite(); | ||||
|             const username = site ? site.getInfo().username : undefined; | ||||
|             // @todo Handle link
 | ||||
| 
 | ||||
|             // Add a class to specify that the iframe is hidden.
 | ||||
| @ -112,8 +104,13 @@ export class CoreIframeUtilsProvider { | ||||
|             return true; | ||||
|         } else if (element.classList.contains('core-iframe-offline-disabled')) { | ||||
|             // Reload the frame.
 | ||||
|             element.src = element.src; | ||||
|             element.data = element.data; | ||||
|             if ('src' in element) { | ||||
|                 // 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
 | ||||
|             CoreDomUtils.instance.removeElement(element.parentElement, 'div.core-iframe-offline-warning'); | ||||
| @ -134,12 +131,14 @@ export class CoreIframeUtilsProvider { | ||||
|      * @param element Element to treat (iframe, embed, ...). | ||||
|      * @return Window and Document. | ||||
|      */ | ||||
|     getContentWindowAndDocument(element: any): { window: Window, document: Document } { | ||||
|         let contentWindow: Window = element.contentWindow; | ||||
|     getContentWindowAndDocument(element: CoreFrameElement): { window: Window; document: Document } { | ||||
|         let contentWindow: Window = 'contentWindow' in element ? element.contentWindow : undefined; | ||||
|         let contentDocument: Document; | ||||
| 
 | ||||
|         try { | ||||
|             contentDocument = element.contentDocument || (contentWindow && contentWindow.document); | ||||
|             contentDocument = 'contentDocument' in element && element.contentDocument | ||||
|                 ? element.contentDocument | ||||
|                 : contentWindow && contentWindow.document; | ||||
|         } catch (ex) { | ||||
|             // Ignore errors.
 | ||||
|         } | ||||
| @ -149,7 +148,7 @@ export class CoreIframeUtilsProvider { | ||||
|             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.
 | ||||
|             try { | ||||
|                 contentDocument = element.getSVGDocument(); | ||||
| @ -187,9 +186,6 @@ export class CoreIframeUtilsProvider { | ||||
|             case 'link_clicked': | ||||
|                 this.linkClicked(event.data.link); | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -202,10 +198,15 @@ export class CoreIframeUtilsProvider { | ||||
|      * @param contentDocument The document of the element contents. | ||||
|      * @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) { | ||||
|             // Intercept window.open.
 | ||||
|             (<any> contentWindow).open = (url: string, name: string): Window => { | ||||
|             contentWindow.open = (url: string, name: string) => { | ||||
|                 this.windowOpen(url, name, element, navCtrl); | ||||
| 
 | ||||
|                 return null; | ||||
| @ -216,7 +217,7 @@ export class CoreIframeUtilsProvider { | ||||
|             // Search sub frames.
 | ||||
|             CoreIframeUtilsProvider.FRAME_TAGS.forEach((tag) => { | ||||
|                 const elements = Array.from(contentDocument.querySelectorAll(tag)); | ||||
|                 elements.forEach((subElement) => { | ||||
|                 elements.forEach((subElement: CoreFrameElement) => { | ||||
|                     this.treatFrame(subElement, true, navCtrl); | ||||
|                 }); | ||||
|             }); | ||||
| @ -231,7 +232,7 @@ export class CoreIframeUtilsProvider { | ||||
|      * @param isSubframe Whether it's a frame inside another frame. | ||||
|      * @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) { | ||||
|             this.checkOnlineFrameInOffline(element, isSubframe); | ||||
| 
 | ||||
| @ -266,7 +267,7 @@ export class CoreIframeUtilsProvider { | ||||
|      * @param element Element to treat (iframe, embed, ...). | ||||
|      * @param contentDocument The document of the element contents. | ||||
|      */ | ||||
|     treatFrameLinks(element: any, contentDocument: Document): void { | ||||
|     treatFrameLinks(element: CoreFrameElement, contentDocument: Document): void { | ||||
|         if (!contentDocument) { | ||||
|             return; | ||||
|         } | ||||
| @ -292,7 +293,7 @@ export class CoreIframeUtilsProvider { | ||||
|             link.treated = true; | ||||
|             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. | ||||
|      * @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); | ||||
|         if (!scheme) { | ||||
|             // 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) { | ||||
|                 const dirAndFile = CoreFile.instance.getFileAndDirectoryFromPath(src); | ||||
|                 if (dirAndFile.directory) { | ||||
| @ -372,8 +375,11 @@ export class CoreIframeUtilsProvider { | ||||
|      * @param event Click event. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async linkClicked(link: {href: string, target?: string}, element?: HTMLFrameElement | HTMLObjectElement, | ||||
|             event?: Event): Promise<void> { | ||||
|     protected async linkClicked( | ||||
|         link: {href: string; target?: string}, | ||||
|         element?: HTMLFrameElement | HTMLObjectElement, | ||||
|         event?: Event, | ||||
|     ): Promise<void> { | ||||
|         if (event && event.defaultPrevented) { | ||||
|             // Event already prevented by some other code.
 | ||||
|             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) {} | ||||
|  | ||||
| @ -13,36 +13,55 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { FileEntry } from '@ionic-native/file'; | ||||
| 
 | ||||
| import { CoreFile } from '@services/file'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { makeSingleton, Translate, Http } from '@singletons/core.singletons'; | ||||
| import { CoreLogger } from '@singletons/logger'; | ||||
| import { CoreWSExternalFile } from '@services/ws'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| 
 | ||||
| interface MimeTypeInfo { | ||||
|     type: string; | ||||
|     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. | ||||
|  */ | ||||
| @Injectable() | ||||
| export class CoreMimetypeUtilsProvider { | ||||
| 
 | ||||
|     protected logger: CoreLogger; | ||||
|     protected extToMime = {}; // Object to map extensions -> mimetypes.
 | ||||
|     protected mimeToExt = {}; // Object to map mimetypes -> extensions.
 | ||||
|     protected groupsMimeInfo = {}; // Object to hold extensions and mimetypes that belong to a certain "group" (audio, video, ...).
 | ||||
|     protected extensionRegex = /^[a-z0-9]+$/; | ||||
|     protected extToMime: Record<string, MimeTypeInfo> = {}; | ||||
|     protected mimeToExt: Record<string, string[]> = {}; | ||||
|     protected groupsMimeInfo: Record<string, MimeTypeGroupInfo> = {}; | ||||
| 
 | ||||
|     constructor() { | ||||
|         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; | ||||
|         }, (err) => { | ||||
|         }, () => { | ||||
|             // 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; | ||||
|         }, (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. | ||||
|      * | ||||
|      * @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 { | ||||
|         let ext; | ||||
|         const filename = file.filename || file.name; | ||||
|     getEmbeddedHtml(file: CoreWSExternalFile | FileEntry, path?: string): string { | ||||
|         const filename = CoreUtils.instance.isFileEntry(file) ? (file as FileEntry).name : file.filename; | ||||
|         const extension = !CoreUtils.instance.isFileEntry(file) && file.mimetype | ||||
|             ? this.getExtension(file.mimetype) | ||||
|             : this.getFileExtension(filename); | ||||
|         const mimeType = !CoreUtils.instance.isFileEntry(file) && file.mimetype ? file.mimetype : this.getMimeType(extension); | ||||
| 
 | ||||
|         if (file.mimetype) { | ||||
|             ext = this.getExtension(file.mimetype); | ||||
|         } else { | ||||
|             ext = this.getFileExtension(filename); | ||||
|             file.mimetype = this.getMimeType(ext); | ||||
|         } | ||||
|         // @todo linting: See if this can be removed
 | ||||
|         (file as CoreWSExternalFile).mimetype = mimeType; | ||||
| 
 | ||||
|         if (this.canBeEmbedded(ext)) { | ||||
|             file.embedType = this.getExtensionType(ext); | ||||
|         if (this.canBeEmbedded(extension)) { | ||||
|             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') { | ||||
|                 return '<img src="' + path + '">'; | ||||
|             } | ||||
|             if (file.embedType == 'audio' || file.embedType == 'video') { | ||||
|                 return '<' + file.embedType + ' controls title="' + filename + '" src="' + path + '">' + | ||||
|                     '<source src="' + path + '" type="' + file.mimetype + '">' + | ||||
|                     '</' + file.embedType + '>'; | ||||
|             path = CoreFile.instance.convertFileSrc(path ?? (CoreUtils.instance.isFileEntry(file) ? file.toURL() : file.fileurl)); | ||||
| 
 | ||||
|             switch (embedType) { | ||||
|                 case 'image': | ||||
|                     return `<img src="${path}">`; | ||||
|                 case 'audio': | ||||
|                 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); | ||||
|             } | ||||
| 
 | ||||
|             if (this.extensionRegex.test(candidate)) { | ||||
|             if (EXTENSION_REGEX.test(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. | ||||
|      * @return Info for the group. | ||||
|      */ | ||||
|     getGroupMimeInfo(group: string, field?: string): any { | ||||
|     getGroupMimeInfo(group: string, field?: string): MimeTypeGroupInfo { | ||||
|         if (typeof this.groupsMimeInfo[group] == 'undefined') { | ||||
|             this.fillGroupMimeInfo(group); | ||||
|         } | ||||
| @ -372,13 +398,13 @@ export class CoreMimetypeUtilsProvider { | ||||
|      * @param capitalise If true, capitalises first character of result. | ||||
|      * @return Type description. | ||||
|      */ | ||||
|     getMimetypeDescription(obj: any, capitalise?: boolean): string { | ||||
|     getMimetypeDescription(obj: FileEntry | { filename: string; mimetype: string } | string, capitalise?: boolean): string { | ||||
|         const langPrefix = 'assets.mimetypes.'; | ||||
|         let filename = ''; | ||||
|         let mimetype = ''; | ||||
|         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.
 | ||||
|             filename = obj.name; | ||||
|         } else if (typeof obj == 'object') { | ||||
| @ -548,6 +574,7 @@ export class CoreMimetypeUtilsProvider { | ||||
| 
 | ||||
|         return path; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class CoreMimetypeUtils extends makeSingleton(CoreMimetypeUtilsProvider) {} | ||||
|  | ||||
| @ -18,6 +18,8 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | ||||
| import { CoreApp } from '@services/app'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| 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. | ||||
| @ -36,53 +38,53 @@ export type CoreTextErrorObject = { | ||||
| export class CoreTextUtilsProvider { | ||||
| 
 | ||||
|     // List of regular expressions to convert the old nomenclature to new nomenclature for disabled features.
 | ||||
|     protected DISABLED_FEATURES_COMPAT_REGEXPS = [ | ||||
|         {old: /\$mmLoginEmailSignup/g, new: 'CoreLoginEmailSignup'}, | ||||
|         {old: /\$mmSideMenuDelegate/g, new: 'CoreMainMenuDelegate'}, | ||||
|         {old: /\$mmCoursesDelegate/g, new: 'CoreCourseOptionsDelegate'}, | ||||
|         {old: /\$mmUserDelegate/g, new: 'CoreUserDelegate'}, | ||||
|         {old: /\$mmCourseDelegate/g, new: 'CoreCourseModuleDelegate'}, | ||||
|         {old: /_mmCourses/g, new: '_CoreCourses'}, | ||||
|         {old: /_mmaFrontpage/g, new: '_CoreSiteHome'}, | ||||
|         {old: /_mmaGrades/g, new: '_CoreGrades'}, | ||||
|         {old: /_mmaCompetency/g, new: '_AddonCompetency'}, | ||||
|         {old: /_mmaNotifications/g, new: '_AddonNotifications'}, | ||||
|         {old: /_mmaMessages/g, new: '_AddonMessages'}, | ||||
|         {old: /_mmaCalendar/g, new: '_AddonCalendar'}, | ||||
|         {old: /_mmaFiles/g, new: '_AddonFiles'}, | ||||
|         {old: /_mmaParticipants/g, new: '_CoreUserParticipants'}, | ||||
|         {old: /_mmaCourseCompletion/g, new: '_AddonCourseCompletion'}, | ||||
|         {old: /_mmaNotes/g, new: '_AddonNotes'}, | ||||
|         {old: /_mmaBadges/g, new: '_AddonBadges'}, | ||||
|         {old: /files_privatefiles/g, new: 'AddonFilesPrivateFiles'}, | ||||
|         {old: /files_sitefiles/g, new: 'AddonFilesSiteFiles'}, | ||||
|         {old: /files_upload/g, new: 'AddonFilesUpload'}, | ||||
|         {old: /_mmaModAssign/g, new: '_AddonModAssign'}, | ||||
|         {old: /_mmaModBook/g, new: '_AddonModBook'}, | ||||
|         {old: /_mmaModChat/g, new: '_AddonModChat'}, | ||||
|         {old: /_mmaModChoice/g, new: '_AddonModChoice'}, | ||||
|         {old: /_mmaModData/g, new: '_AddonModData'}, | ||||
|         {old: /_mmaModFeedback/g, new: '_AddonModFeedback'}, | ||||
|         {old: /_mmaModFolder/g, new: '_AddonModFolder'}, | ||||
|         {old: /_mmaModForum/g, new: '_AddonModForum'}, | ||||
|         {old: /_mmaModGlossary/g, new: '_AddonModGlossary'}, | ||||
|         {old: /_mmaModH5pactivity/g, new: '_AddonModH5PActivity'}, | ||||
|         {old: /_mmaModImscp/g, new: '_AddonModImscp'}, | ||||
|         {old: /_mmaModLabel/g, new: '_AddonModLabel'}, | ||||
|         {old: /_mmaModLesson/g, new: '_AddonModLesson'}, | ||||
|         {old: /_mmaModLti/g, new: '_AddonModLti'}, | ||||
|         {old: /_mmaModPage/g, new: '_AddonModPage'}, | ||||
|         {old: /_mmaModQuiz/g, new: '_AddonModQuiz'}, | ||||
|         {old: /_mmaModResource/g, new: '_AddonModResource'}, | ||||
|         {old: /_mmaModScorm/g, new: '_AddonModScorm'}, | ||||
|         {old: /_mmaModSurvey/g, new: '_AddonModSurvey'}, | ||||
|         {old: /_mmaModUrl/g, new: '_AddonModUrl'}, | ||||
|         {old: /_mmaModWiki/g, new: '_AddonModWiki'}, | ||||
|         {old: /_mmaModWorkshop/g, new: '_AddonModWorkshop'}, | ||||
|         {old: /remoteAddOn_/g, new: 'sitePlugin_'}, | ||||
|     protected readonly DISABLED_FEATURES_COMPAT_REGEXPS: { old: RegExp; new: string }[] = [ | ||||
|         { old: /\$mmLoginEmailSignup/g, new: 'CoreLoginEmailSignup' }, | ||||
|         { old: /\$mmSideMenuDelegate/g, new: 'CoreMainMenuDelegate' }, | ||||
|         { old: /\$mmCoursesDelegate/g, new: 'CoreCourseOptionsDelegate' }, | ||||
|         { old: /\$mmUserDelegate/g, new: 'CoreUserDelegate' }, | ||||
|         { old: /\$mmCourseDelegate/g, new: 'CoreCourseModuleDelegate' }, | ||||
|         { old: /_mmCourses/g, new: '_CoreCourses' }, | ||||
|         { old: /_mmaFrontpage/g, new: '_CoreSiteHome' }, | ||||
|         { old: /_mmaGrades/g, new: '_CoreGrades' }, | ||||
|         { old: /_mmaCompetency/g, new: '_AddonCompetency' }, | ||||
|         { old: /_mmaNotifications/g, new: '_AddonNotifications' }, | ||||
|         { old: /_mmaMessages/g, new: '_AddonMessages' }, | ||||
|         { old: /_mmaCalendar/g, new: '_AddonCalendar' }, | ||||
|         { old: /_mmaFiles/g, new: '_AddonFiles' }, | ||||
|         { old: /_mmaParticipants/g, new: '_CoreUserParticipants' }, | ||||
|         { old: /_mmaCourseCompletion/g, new: '_AddonCourseCompletion' }, | ||||
|         { old: /_mmaNotes/g, new: '_AddonNotes' }, | ||||
|         { old: /_mmaBadges/g, new: '_AddonBadges' }, | ||||
|         { old: /files_privatefiles/g, new: 'AddonFilesPrivateFiles' }, | ||||
|         { old: /files_sitefiles/g, new: 'AddonFilesSiteFiles' }, | ||||
|         { old: /files_upload/g, new: 'AddonFilesUpload' }, | ||||
|         { old: /_mmaModAssign/g, new: '_AddonModAssign' }, | ||||
|         { old: /_mmaModBook/g, new: '_AddonModBook' }, | ||||
|         { old: /_mmaModChat/g, new: '_AddonModChat' }, | ||||
|         { old: /_mmaModChoice/g, new: '_AddonModChoice' }, | ||||
|         { old: /_mmaModData/g, new: '_AddonModData' }, | ||||
|         { old: /_mmaModFeedback/g, new: '_AddonModFeedback' }, | ||||
|         { old: /_mmaModFolder/g, new: '_AddonModFolder' }, | ||||
|         { old: /_mmaModForum/g, new: '_AddonModForum' }, | ||||
|         { old: /_mmaModGlossary/g, new: '_AddonModGlossary' }, | ||||
|         { old: /_mmaModH5pactivity/g, new: '_AddonModH5PActivity' }, | ||||
|         { old: /_mmaModImscp/g, new: '_AddonModImscp' }, | ||||
|         { old: /_mmaModLabel/g, new: '_AddonModLabel' }, | ||||
|         { old: /_mmaModLesson/g, new: '_AddonModLesson' }, | ||||
|         { old: /_mmaModLti/g, new: '_AddonModLti' }, | ||||
|         { old: /_mmaModPage/g, new: '_AddonModPage' }, | ||||
|         { old: /_mmaModQuiz/g, new: '_AddonModQuiz' }, | ||||
|         { old: /_mmaModResource/g, new: '_AddonModResource' }, | ||||
|         { old: /_mmaModScorm/g, new: '_AddonModScorm' }, | ||||
|         { old: /_mmaModSurvey/g, new: '_AddonModSurvey' }, | ||||
|         { old: /_mmaModUrl/g, new: '_AddonModUrl' }, | ||||
|         { old: /_mmaModWiki/g, new: '_AddonModWiki' }, | ||||
|         { old: /_mmaModWorkshop/g, new: '_AddonModWorkshop' }, | ||||
|         { 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) { } | ||||
| 
 | ||||
| @ -200,7 +202,6 @@ export class CoreTextUtilsProvider { | ||||
|      * @return Size in human readable format. | ||||
|      */ | ||||
|     bytesToSize(bytes: number, precision: number = 2): string { | ||||
| 
 | ||||
|         if (typeof bytes == 'undefined' || bytes === null || bytes < 0) { | ||||
|             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. | ||||
|      * @deprecated since 3.8.3. Please use viewText instead. | ||||
|      */ | ||||
|     expandText(title: string, text: string, component?: string, componentId?: string | number, files?: any[], | ||||
|             filter?: boolean, contextLevel?: string, instanceId?: number, courseId?: number): void { | ||||
| 
 | ||||
|     expandText( | ||||
|         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, { | ||||
|             component, | ||||
|             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. | ||||
|      * @return Pluginfile URL, undefined if no files found. | ||||
|      */ | ||||
|     getTextPluginfileUrl(files: any[]): string { | ||||
|     getTextPluginfileUrl(files: CoreWSExternalFile[]): string { | ||||
|         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).
 | ||||
|             return fileURL.substr(0, Math.max(fileURL.lastIndexOf('/'), fileURL.lastIndexOf('%2F'))); | ||||
|             return url.substr(0, Math.max(url.lastIndexOf('/'), url.lastIndexOf('%2F'))); | ||||
|         } | ||||
| 
 | ||||
|         return undefined; | ||||
| @ -610,13 +619,17 @@ export class CoreTextUtilsProvider { | ||||
|      * @param data Object to be checked. | ||||
|      * @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) { | ||||
|             if (typeof data[el] == 'object') { | ||||
|                 if (this.hasUnicodeData(data[el])) { | ||||
|                 if (this.hasUnicodeData(data[el] as Record<string, unknown>)) { | ||||
|                     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; | ||||
|             } | ||||
|         } | ||||
| @ -628,17 +641,17 @@ export class CoreTextUtilsProvider { | ||||
|      * Same as Javascript's JSON.parse, but it will handle errors. | ||||
|      * | ||||
|      * @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. | ||||
|      * @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 { | ||||
|             return JSON.parse(json); | ||||
|         } catch (ex) { | ||||
|         } catch (error) { | ||||
|             // Error, log the error if needed.
 | ||||
|             if (logErrorFn) { | ||||
|                 logErrorFn(ex); | ||||
|                 logErrorFn(error); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -675,7 +688,7 @@ export class CoreTextUtilsProvider { | ||||
|             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. | ||||
|      * @return Treated text. | ||||
|      */ | ||||
|     replacePluginfileUrls(text: string, files: any[]): string { | ||||
|     replacePluginfileUrls(text: string, files: CoreWSExternalFile[]): string { | ||||
|         if (text && typeof text == 'string') { | ||||
|             const fileURL = this.getTextPluginfileUrl(files); | ||||
|             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. | ||||
|      * @return Treated text. | ||||
|      */ | ||||
|     restorePluginfileUrls(text: string, files: any[]): string { | ||||
|     restorePluginfileUrls(text: string, files: CoreWSExternalFile[]): string { | ||||
|         if (text && typeof text == 'string') { | ||||
|             const fileURL = this.getTextPluginfileUrl(files); | ||||
|             if (fileURL) { | ||||
| @ -804,7 +817,6 @@ export class CoreTextUtilsProvider { | ||||
| 
 | ||||
|     /** | ||||
|      * 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 replace The value to put inside the string. | ||||
| @ -814,22 +826,7 @@ export class CoreTextUtilsProvider { | ||||
|      * @return Treated string. | ||||
|      */ | ||||
|     substrReplace(str: string, replace: string, start: number, length?: number): string { | ||||
|         length = typeof length != 'undefined' ? length : str.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(''); | ||||
|         return Locutus.substrReplace(str, replace, start, length); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -867,14 +864,14 @@ export class CoreTextUtilsProvider { | ||||
|         return CoreLang.instance.getCurrentLanguage().then((language) => { | ||||
|             // Match the current language.
 | ||||
|             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)) { | ||||
|                 // Current lang not found. Try to find the first language.
 | ||||
|                 const matches = text.match(anyLangRegEx); | ||||
|                 if (matches && matches[0]) { | ||||
|                     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 { | ||||
|                     // No multi-lang tag found, stop.
 | ||||
|                     return text; | ||||
| @ -915,221 +912,12 @@ export class CoreTextUtilsProvider { | ||||
| 
 | ||||
|     /** | ||||
|      * Unserialize Array from PHP. | ||||
|      * Taken from: https://github.com/kvz/locutus/blob/master/src/php/var/unserialize.js
 | ||||
|      * | ||||
|      * @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. | ||||
|      */ | ||||
|     unserialize(data: string, logErrorFn?: (error?: string) => void): any { | ||||
|         //  Discuss at: http://locutus.io/php/unserialize/
 | ||||
|         // 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]; | ||||
|     unserialize<T = unknown>(data: string): T { | ||||
|         return Locutus.unserialize<T>(data); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1138,16 +926,13 @@ export class CoreTextUtilsProvider { | ||||
|      * @param title Title of the new state. | ||||
|      * @param text Content of the text to be expanded. | ||||
|      * @param component Component to link the embedded files to. | ||||
|      * @param componentId An ID to use in conjunction with the component. | ||||
|      * @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. | ||||
|      * @param options Options. | ||||
|      */ | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||
|     viewText(title: string, text: string, options?: CoreTextUtilsViewTextOptions): void { | ||||
|         // @todo
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| @ -1156,7 +941,7 @@ export class CoreTextUtilsProvider { | ||||
| export type CoreTextUtilsViewTextOptions = { | ||||
|     component?: string; // Component to link the embedded files to.
 | ||||
|     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.
 | ||||
|     contextLevel?: string; // The context level.
 | ||||
|     instanceId?: number; // The instance ID related to the context.
 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										447
									
								
								src/app/singletons/locutus.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										447
									
								
								src/app/singletons/locutus.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
							
								
								
									
										1272
									
								
								src/assets/exttomime.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1272
									
								
								src/assets/exttomime.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1095
									
								
								src/assets/mimetoext.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1095
									
								
								src/assets/mimetoext.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -3,8 +3,9 @@ | ||||
|   "compilerOptions": { | ||||
|     "outDir": "./out-tsc/app", | ||||
|     "types": [ | ||||
|       "cordova", | ||||
|       "cordova-plugin-file-transfer", | ||||
|       "cordova-plugin-inappbrowser", | ||||
|       "cordova", | ||||
|       "node" | ||||
|     ], | ||||
|     "paths": { | ||||
|  | ||||
| @ -18,6 +18,9 @@ | ||||
|       "dom" | ||||
|     ], | ||||
|     "types": [ | ||||
|         "cordova-plugin-file-transfer", | ||||
|         "cordova-plugin-inappbrowser", | ||||
|         "cordova", | ||||
|         "jest", | ||||
|         "node" | ||||
|     ], | ||||
|  | ||||
| @ -6,6 +6,9 @@ | ||||
|     "emitDecoratorMetadata": true, | ||||
|     "outDir": "./out-tsc/tests", | ||||
|     "types": [ | ||||
|       "cordova-plugin-file-transfer", | ||||
|       "cordova-plugin-inappbrowser", | ||||
|       "cordova", | ||||
|       "jest", | ||||
|       "node" | ||||
|     ], | ||||
|  | ||||
							
								
								
									
										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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user