MOBILE-3320 lint: Fix linting in text service
This commit is contained in:
		
							parent
							
								
									0ca1a37b1b
								
							
						
					
					
						commit
						eed59972a8
					
				| @ -18,6 +18,8 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | |||||||
| import { CoreApp } from '@services/app'; | import { CoreApp } from '@services/app'; | ||||||
| import { CoreLang } from '@services/lang'; | import { CoreLang } from '@services/lang'; | ||||||
| import { makeSingleton, Translate } from '@singletons/core.singletons'; | import { makeSingleton, Translate } from '@singletons/core.singletons'; | ||||||
|  | import { CoreWSExternalFile } from '@services/ws'; | ||||||
|  | import { Locutus } from '@singletons/locutus'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Different type of errors the app can treat. |  * Different type of errors the app can treat. | ||||||
| @ -36,53 +38,53 @@ export type CoreTextErrorObject = { | |||||||
| export class CoreTextUtilsProvider { | export class CoreTextUtilsProvider { | ||||||
| 
 | 
 | ||||||
|     // List of regular expressions to convert the old nomenclature to new nomenclature for disabled features.
 |     // List of regular expressions to convert the old nomenclature to new nomenclature for disabled features.
 | ||||||
|     protected DISABLED_FEATURES_COMPAT_REGEXPS = [ |     protected readonly DISABLED_FEATURES_COMPAT_REGEXPS: { old: RegExp; new: string }[] = [ | ||||||
|         {old: /\$mmLoginEmailSignup/g, new: 'CoreLoginEmailSignup'}, |         { old: /\$mmLoginEmailSignup/g, new: 'CoreLoginEmailSignup' }, | ||||||
|         {old: /\$mmSideMenuDelegate/g, new: 'CoreMainMenuDelegate'}, |         { old: /\$mmSideMenuDelegate/g, new: 'CoreMainMenuDelegate' }, | ||||||
|         {old: /\$mmCoursesDelegate/g, new: 'CoreCourseOptionsDelegate'}, |         { old: /\$mmCoursesDelegate/g, new: 'CoreCourseOptionsDelegate' }, | ||||||
|         {old: /\$mmUserDelegate/g, new: 'CoreUserDelegate'}, |         { old: /\$mmUserDelegate/g, new: 'CoreUserDelegate' }, | ||||||
|         {old: /\$mmCourseDelegate/g, new: 'CoreCourseModuleDelegate'}, |         { old: /\$mmCourseDelegate/g, new: 'CoreCourseModuleDelegate' }, | ||||||
|         {old: /_mmCourses/g, new: '_CoreCourses'}, |         { old: /_mmCourses/g, new: '_CoreCourses' }, | ||||||
|         {old: /_mmaFrontpage/g, new: '_CoreSiteHome'}, |         { old: /_mmaFrontpage/g, new: '_CoreSiteHome' }, | ||||||
|         {old: /_mmaGrades/g, new: '_CoreGrades'}, |         { old: /_mmaGrades/g, new: '_CoreGrades' }, | ||||||
|         {old: /_mmaCompetency/g, new: '_AddonCompetency'}, |         { old: /_mmaCompetency/g, new: '_AddonCompetency' }, | ||||||
|         {old: /_mmaNotifications/g, new: '_AddonNotifications'}, |         { old: /_mmaNotifications/g, new: '_AddonNotifications' }, | ||||||
|         {old: /_mmaMessages/g, new: '_AddonMessages'}, |         { old: /_mmaMessages/g, new: '_AddonMessages' }, | ||||||
|         {old: /_mmaCalendar/g, new: '_AddonCalendar'}, |         { old: /_mmaCalendar/g, new: '_AddonCalendar' }, | ||||||
|         {old: /_mmaFiles/g, new: '_AddonFiles'}, |         { old: /_mmaFiles/g, new: '_AddonFiles' }, | ||||||
|         {old: /_mmaParticipants/g, new: '_CoreUserParticipants'}, |         { old: /_mmaParticipants/g, new: '_CoreUserParticipants' }, | ||||||
|         {old: /_mmaCourseCompletion/g, new: '_AddonCourseCompletion'}, |         { old: /_mmaCourseCompletion/g, new: '_AddonCourseCompletion' }, | ||||||
|         {old: /_mmaNotes/g, new: '_AddonNotes'}, |         { old: /_mmaNotes/g, new: '_AddonNotes' }, | ||||||
|         {old: /_mmaBadges/g, new: '_AddonBadges'}, |         { old: /_mmaBadges/g, new: '_AddonBadges' }, | ||||||
|         {old: /files_privatefiles/g, new: 'AddonFilesPrivateFiles'}, |         { old: /files_privatefiles/g, new: 'AddonFilesPrivateFiles' }, | ||||||
|         {old: /files_sitefiles/g, new: 'AddonFilesSiteFiles'}, |         { old: /files_sitefiles/g, new: 'AddonFilesSiteFiles' }, | ||||||
|         {old: /files_upload/g, new: 'AddonFilesUpload'}, |         { old: /files_upload/g, new: 'AddonFilesUpload' }, | ||||||
|         {old: /_mmaModAssign/g, new: '_AddonModAssign'}, |         { old: /_mmaModAssign/g, new: '_AddonModAssign' }, | ||||||
|         {old: /_mmaModBook/g, new: '_AddonModBook'}, |         { old: /_mmaModBook/g, new: '_AddonModBook' }, | ||||||
|         {old: /_mmaModChat/g, new: '_AddonModChat'}, |         { old: /_mmaModChat/g, new: '_AddonModChat' }, | ||||||
|         {old: /_mmaModChoice/g, new: '_AddonModChoice'}, |         { old: /_mmaModChoice/g, new: '_AddonModChoice' }, | ||||||
|         {old: /_mmaModData/g, new: '_AddonModData'}, |         { old: /_mmaModData/g, new: '_AddonModData' }, | ||||||
|         {old: /_mmaModFeedback/g, new: '_AddonModFeedback'}, |         { old: /_mmaModFeedback/g, new: '_AddonModFeedback' }, | ||||||
|         {old: /_mmaModFolder/g, new: '_AddonModFolder'}, |         { old: /_mmaModFolder/g, new: '_AddonModFolder' }, | ||||||
|         {old: /_mmaModForum/g, new: '_AddonModForum'}, |         { old: /_mmaModForum/g, new: '_AddonModForum' }, | ||||||
|         {old: /_mmaModGlossary/g, new: '_AddonModGlossary'}, |         { old: /_mmaModGlossary/g, new: '_AddonModGlossary' }, | ||||||
|         {old: /_mmaModH5pactivity/g, new: '_AddonModH5PActivity'}, |         { old: /_mmaModH5pactivity/g, new: '_AddonModH5PActivity' }, | ||||||
|         {old: /_mmaModImscp/g, new: '_AddonModImscp'}, |         { old: /_mmaModImscp/g, new: '_AddonModImscp' }, | ||||||
|         {old: /_mmaModLabel/g, new: '_AddonModLabel'}, |         { old: /_mmaModLabel/g, new: '_AddonModLabel' }, | ||||||
|         {old: /_mmaModLesson/g, new: '_AddonModLesson'}, |         { old: /_mmaModLesson/g, new: '_AddonModLesson' }, | ||||||
|         {old: /_mmaModLti/g, new: '_AddonModLti'}, |         { old: /_mmaModLti/g, new: '_AddonModLti' }, | ||||||
|         {old: /_mmaModPage/g, new: '_AddonModPage'}, |         { old: /_mmaModPage/g, new: '_AddonModPage' }, | ||||||
|         {old: /_mmaModQuiz/g, new: '_AddonModQuiz'}, |         { old: /_mmaModQuiz/g, new: '_AddonModQuiz' }, | ||||||
|         {old: /_mmaModResource/g, new: '_AddonModResource'}, |         { old: /_mmaModResource/g, new: '_AddonModResource' }, | ||||||
|         {old: /_mmaModScorm/g, new: '_AddonModScorm'}, |         { old: /_mmaModScorm/g, new: '_AddonModScorm' }, | ||||||
|         {old: /_mmaModSurvey/g, new: '_AddonModSurvey'}, |         { old: /_mmaModSurvey/g, new: '_AddonModSurvey' }, | ||||||
|         {old: /_mmaModUrl/g, new: '_AddonModUrl'}, |         { old: /_mmaModUrl/g, new: '_AddonModUrl' }, | ||||||
|         {old: /_mmaModWiki/g, new: '_AddonModWiki'}, |         { old: /_mmaModWiki/g, new: '_AddonModWiki' }, | ||||||
|         {old: /_mmaModWorkshop/g, new: '_AddonModWorkshop'}, |         { old: /_mmaModWorkshop/g, new: '_AddonModWorkshop' }, | ||||||
|         {old: /remoteAddOn_/g, new: 'sitePlugin_'}, |         { old: /remoteAddOn_/g, new: 'sitePlugin_' }, | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     protected template = document.createElement('template'); // A template element to convert HTML to element.
 |     protected template: HTMLTemplateElement = document.createElement('template'); // A template element to convert HTML to element.
 | ||||||
| 
 | 
 | ||||||
|     constructor(private sanitizer: DomSanitizer) { } |     constructor(private sanitizer: DomSanitizer) { } | ||||||
| 
 | 
 | ||||||
| @ -200,7 +202,6 @@ export class CoreTextUtilsProvider { | |||||||
|      * @return Size in human readable format. |      * @return Size in human readable format. | ||||||
|      */ |      */ | ||||||
|     bytesToSize(bytes: number, precision: number = 2): string { |     bytesToSize(bytes: number, precision: number = 2): string { | ||||||
| 
 |  | ||||||
|         if (typeof bytes == 'undefined' || bytes === null || bytes < 0) { |         if (typeof bytes == 'undefined' || bytes === null || bytes < 0) { | ||||||
|             return Translate.instance.instant('core.notapplicable'); |             return Translate.instance.instant('core.notapplicable'); | ||||||
|         } |         } | ||||||
| @ -449,9 +450,8 @@ export class CoreTextUtilsProvider { | |||||||
|      * @param courseId Course ID the text belongs to. It can be used to improve performance with filters. |      * @param courseId Course ID the text belongs to. It can be used to improve performance with filters. | ||||||
|      * @deprecated since 3.8.3. Please use viewText instead. |      * @deprecated since 3.8.3. Please use viewText instead. | ||||||
|      */ |      */ | ||||||
|     expandText(title: string, text: string, component?: string, componentId?: string | number, files?: any[], |     expandText(title: string, text: string, component?: string, componentId?: string | number, files?: CoreWSExternalFile[], | ||||||
|             filter?: boolean, contextLevel?: string, instanceId?: number, courseId?: number): void { |             filter?: boolean, contextLevel?: string, instanceId?: number, courseId?: number): void { | ||||||
| 
 |  | ||||||
|         return this.viewText(title, text, { |         return this.viewText(title, text, { | ||||||
|             component, |             component, | ||||||
|             componentId, |             componentId, | ||||||
| @ -531,12 +531,12 @@ export class CoreTextUtilsProvider { | |||||||
|      * @param files Files to extract the URL from. They need to have the URL in a 'url' or 'fileurl' attribute. |      * @param files Files to extract the URL from. They need to have the URL in a 'url' or 'fileurl' attribute. | ||||||
|      * @return Pluginfile URL, undefined if no files found. |      * @return Pluginfile URL, undefined if no files found. | ||||||
|      */ |      */ | ||||||
|     getTextPluginfileUrl(files: any[]): string { |     getTextPluginfileUrl(files: CoreWSExternalFile[]): string { | ||||||
|         if (files && files.length) { |         if (files && files.length) { | ||||||
|             const fileURL = files[0].url || files[0].fileurl; |             const url = files[0].fileurl; | ||||||
| 
 | 
 | ||||||
|             // Remove text after last slash (encoded or not).
 |             // Remove text after last slash (encoded or not).
 | ||||||
|             return fileURL.substr(0, Math.max(fileURL.lastIndexOf('/'), fileURL.lastIndexOf('%2F'))); |             return url.substr(0, Math.max(url.lastIndexOf('/'), url.lastIndexOf('%2F'))); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return undefined; |         return undefined; | ||||||
| @ -610,13 +610,17 @@ export class CoreTextUtilsProvider { | |||||||
|      * @param data Object to be checked. |      * @param data Object to be checked. | ||||||
|      * @return If the data has any long Unicode char on it. |      * @return If the data has any long Unicode char on it. | ||||||
|      */ |      */ | ||||||
|     hasUnicodeData(data: object): boolean { |     hasUnicodeData(data: Record<string, unknown>): boolean { | ||||||
|         for (const el in data) { |         for (const el in data) { | ||||||
|             if (typeof data[el] == 'object') { |             if (typeof data[el] == 'object') { | ||||||
|                 if (this.hasUnicodeData(data[el])) { |                 if (this.hasUnicodeData(data[el] as Record<string, unknown>)) { | ||||||
|                     return true; |                     return true; | ||||||
|                 } |                 } | ||||||
|             } else if (typeof data[el] == 'string' && this.hasUnicode(data[el])) { | 
 | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (typeof data[el] == 'string' && this.hasUnicode(data[el] as string)) { | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -628,17 +632,17 @@ export class CoreTextUtilsProvider { | |||||||
|      * Same as Javascript's JSON.parse, but it will handle errors. |      * Same as Javascript's JSON.parse, but it will handle errors. | ||||||
|      * |      * | ||||||
|      * @param json JSON text. |      * @param json JSON text. | ||||||
|      * @param defaultValue Default value t oreturn if the parse fails. Defaults to the original value. |      * @param defaultValue Default value to return if the parse fails. Defaults to the original value. | ||||||
|      * @param logErrorFn An error to call with the exception to log the error. If not supplied, no error. |      * @param logErrorFn An error to call with the exception to log the error. If not supplied, no error. | ||||||
|      * @return JSON parsed as object or what it gets. |      * @return JSON parsed as object or what it gets. | ||||||
|      */ |      */ | ||||||
|     parseJSON(json: string, defaultValue?: any, logErrorFn?: (error?: any) => void): any { |     parseJSON<T>(json: string, defaultValue?: T, logErrorFn?: (error?: Error) => void): T | string { | ||||||
|         try { |         try { | ||||||
|             return JSON.parse(json); |             return JSON.parse(json); | ||||||
|         } catch (ex) { |         } catch (error) { | ||||||
|             // Error, log the error if needed.
 |             // Error, log the error if needed.
 | ||||||
|             if (logErrorFn) { |             if (logErrorFn) { | ||||||
|                 logErrorFn(ex); |                 logErrorFn(error); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -675,7 +679,7 @@ export class CoreTextUtilsProvider { | |||||||
|             return ''; |             return ''; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return text.replace(/[#:\/\?\\]+/g, '_'); |         return text.replace(/[#:/?\\]+/g, '_'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -700,7 +704,7 @@ export class CoreTextUtilsProvider { | |||||||
|      * @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute. |      * @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute. | ||||||
|      * @return Treated text. |      * @return Treated text. | ||||||
|      */ |      */ | ||||||
|     replacePluginfileUrls(text: string, files: any[]): string { |     replacePluginfileUrls(text: string, files: CoreWSExternalFile[]): string { | ||||||
|         if (text && typeof text == 'string') { |         if (text && typeof text == 'string') { | ||||||
|             const fileURL = this.getTextPluginfileUrl(files); |             const fileURL = this.getTextPluginfileUrl(files); | ||||||
|             if (fileURL) { |             if (fileURL) { | ||||||
| @ -718,7 +722,7 @@ export class CoreTextUtilsProvider { | |||||||
|      * @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute. |      * @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute. | ||||||
|      * @return Treated text. |      * @return Treated text. | ||||||
|      */ |      */ | ||||||
|     restorePluginfileUrls(text: string, files: any[]): string { |     restorePluginfileUrls(text: string, files: CoreWSExternalFile[]): string { | ||||||
|         if (text && typeof text == 'string') { |         if (text && typeof text == 'string') { | ||||||
|             const fileURL = this.getTextPluginfileUrl(files); |             const fileURL = this.getTextPluginfileUrl(files); | ||||||
|             if (fileURL) { |             if (fileURL) { | ||||||
| @ -804,7 +808,6 @@ export class CoreTextUtilsProvider { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Replace text within a portion of a string. Equivalent to PHP's substr_replace. |      * Replace text within a portion of a string. Equivalent to PHP's substr_replace. | ||||||
|      * Credits to http://locutus.io/php/strings/substr_replace/
 |  | ||||||
|      * |      * | ||||||
|      * @param str The string to treat. |      * @param str The string to treat. | ||||||
|      * @param replace The value to put inside the string. |      * @param replace The value to put inside the string. | ||||||
| @ -814,22 +817,7 @@ export class CoreTextUtilsProvider { | |||||||
|      * @return Treated string. |      * @return Treated string. | ||||||
|      */ |      */ | ||||||
|     substrReplace(str: string, replace: string, start: number, length?: number): string { |     substrReplace(str: string, replace: string, start: number, length?: number): string { | ||||||
|         length = typeof length != 'undefined' ? length : str.length; |         return Locutus.substrReplace(str, replace, start, length); | ||||||
| 
 |  | ||||||
|         if (start < 0) { |  | ||||||
|             start = start + str.length; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (length < 0) { |  | ||||||
|             length = length + str.length - start; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return [ |  | ||||||
|             str.slice(0, start), |  | ||||||
|             replace.substr(0, length), |  | ||||||
|             replace.slice(length), |  | ||||||
|             str.slice(start + length) |  | ||||||
|         ].join(''); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -867,14 +855,14 @@ export class CoreTextUtilsProvider { | |||||||
|         return CoreLang.instance.getCurrentLanguage().then((language) => { |         return CoreLang.instance.getCurrentLanguage().then((language) => { | ||||||
|             // Match the current language.
 |             // Match the current language.
 | ||||||
|             const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g; |             const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g; | ||||||
|             let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g'); |             let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)</(?:lang|span)>', 'g'); | ||||||
| 
 | 
 | ||||||
|             if (!text.match(currentLangRegEx)) { |             if (!text.match(currentLangRegEx)) { | ||||||
|                 // Current lang not found. Try to find the first language.
 |                 // Current lang not found. Try to find the first language.
 | ||||||
|                 const matches = text.match(anyLangRegEx); |                 const matches = text.match(anyLangRegEx); | ||||||
|                 if (matches && matches[0]) { |                 if (matches && matches[0]) { | ||||||
|                     language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)[1]; |                     language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)[1]; | ||||||
|                     currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g'); |                     currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)</(?:lang|span)>', 'g'); | ||||||
|                 } else { |                 } else { | ||||||
|                     // No multi-lang tag found, stop.
 |                     // No multi-lang tag found, stop.
 | ||||||
|                     return text; |                     return text; | ||||||
| @ -915,221 +903,12 @@ export class CoreTextUtilsProvider { | |||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Unserialize Array from PHP. |      * Unserialize Array from PHP. | ||||||
|      * Taken from: https://github.com/kvz/locutus/blob/master/src/php/var/unserialize.js
 |  | ||||||
|      * |      * | ||||||
|      * @param data String to unserialize. |      * @param data String to unserialize. | ||||||
|      * @param logErrorFn An error to call with the exception to log the error. If not supplied, no error. |  | ||||||
|      * @return Unserialized data. |      * @return Unserialized data. | ||||||
|      */ |      */ | ||||||
|     unserialize(data: string, logErrorFn?: (error?: string) => void): any { |     unserialize<T = unknown>(data: string): T { | ||||||
|         //  Discuss at: http://locutus.io/php/unserialize/
 |         return Locutus.unserialize<T>(data); | ||||||
|         // Original by: Arpad Ray (mailto:arpad@php.net)
 |  | ||||||
|         // Improved by: Pedro Tainha (http://www.pedrotainha.com)
 |  | ||||||
|         // Improved by: Kevin van Zonneveld (http://kvz.io)
 |  | ||||||
|         // Improved by: Kevin van Zonneveld (http://kvz.io)
 |  | ||||||
|         // Improved by: Chris
 |  | ||||||
|         // Improved by: James
 |  | ||||||
|         // Improved by: Le Torbi
 |  | ||||||
|         // Improved by: Eli Skeggs
 |  | ||||||
|         // Bugfixed by: dptr1988
 |  | ||||||
|         // Bugfixed by: Kevin van Zonneveld (http://kvz.io)
 |  | ||||||
|         // Bugfixed by: Brett Zamir (http://brett-zamir.me)
 |  | ||||||
|         // Bugfixed by: philippsimon (https://github.com/philippsimon/)
 |  | ||||||
|         //  Revised by: d3x
 |  | ||||||
|         //    Input by: Brett Zamir (http://brett-zamir.me)
 |  | ||||||
|         //    Input by: Martin (http://www.erlenwiese.de/)
 |  | ||||||
|         //    Input by: kilops
 |  | ||||||
|         //    Input by: Jaroslaw Czarniak
 |  | ||||||
|         //    Input by: lovasoa (https://github.com/lovasoa/)
 |  | ||||||
|         //      Note 1: We feel the main purpose of this function should be
 |  | ||||||
|         //      Note 1: to ease the transport of data between php & js
 |  | ||||||
|         //      Note 1: Aiming for PHP-compatibility, we have to translate objects to arrays
 |  | ||||||
|         //   Example 1: unserialize('a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}')
 |  | ||||||
|         //   Returns 1: ['Kevin', 'van', 'Zonneveld']
 |  | ||||||
|         //   Example 2: unserialize('a:2:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";}')
 |  | ||||||
|         //   Returns 2: {firstName: 'Kevin', midName: 'van'}
 |  | ||||||
|         //   Example 3: unserialize('a:3:{s:2:"ü";s:2:"ü";s:3:"四";s:3:"四";s:4:"𠜎";s:4:"𠜎";}')
 |  | ||||||
|         //   Returns 3: {'ü': 'ü', '四': '四', '𠜎': '𠜎'}
 |  | ||||||
| 
 |  | ||||||
|         const utf8Overhead = (str: string): number => { |  | ||||||
|             let s = str.length; |  | ||||||
| 
 |  | ||||||
|             for (let i = str.length - 1; i >= 0; i--) { |  | ||||||
|                 const code = str.charCodeAt(i); |  | ||||||
|                 if (code > 0x7f && code <= 0x7ff) { |  | ||||||
|                     s++; |  | ||||||
|                 } else if (code > 0x7ff && code <= 0xffff) { |  | ||||||
|                     s += 2; |  | ||||||
|                 } |  | ||||||
|                 // Trail surrogate.
 |  | ||||||
|                 if (code >= 0xDC00 && code <= 0xDFFF) { |  | ||||||
|                     i--; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return s - 1; |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         const error = (type: string, msg: string): void => { |  | ||||||
|             if (logErrorFn) { |  | ||||||
|                 logErrorFn(type + msg); |  | ||||||
|             } |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         const readUntil = (data: string, offset: number, stopchr: string): Array<any> => { |  | ||||||
|             let i = 2; |  | ||||||
|             const buf = []; |  | ||||||
|             let chr = data.slice(offset, offset + 1); |  | ||||||
| 
 |  | ||||||
|             while (chr !== stopchr) { |  | ||||||
|                 if ((i + offset) > data.length) { |  | ||||||
|                     error('Error', 'Invalid'); |  | ||||||
|                 } |  | ||||||
|                 buf.push(chr); |  | ||||||
|                 chr = data.slice(offset + (i - 1), offset + i); |  | ||||||
|                 i += 1; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return [buf.length, buf.join('')]; |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         const readChrs = (data: string, offset: number, length: number): Array<any> => { |  | ||||||
|             let chr; |  | ||||||
|             const buf = []; |  | ||||||
| 
 |  | ||||||
|             for (let i = 0; i < length; i++) { |  | ||||||
|                 chr = data.slice(offset + (i - 1), offset + i); |  | ||||||
|                 buf.push(chr); |  | ||||||
|                 length -= utf8Overhead(chr); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return [buf.length, buf.join('')]; |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         const _unserialize = (data: string, offset: number): any => { |  | ||||||
|             let dtype, |  | ||||||
|                 dataoffset, |  | ||||||
|                 keyandchrs, |  | ||||||
|                 keys, |  | ||||||
|                 contig, |  | ||||||
|                 length, |  | ||||||
|                 array, |  | ||||||
|                 readdata, |  | ||||||
|                 readData, |  | ||||||
|                 ccount, |  | ||||||
|                 stringlength, |  | ||||||
|                 i, |  | ||||||
|                 key, |  | ||||||
|                 kprops, |  | ||||||
|                 kchrs, |  | ||||||
|                 vprops, |  | ||||||
|                 vchrs, |  | ||||||
|                 value, |  | ||||||
|                 chrs = 0, |  | ||||||
|                 typeconvert = (x: any): any => { |  | ||||||
|                     return x; |  | ||||||
|                 }; |  | ||||||
| 
 |  | ||||||
|             if (!offset) { |  | ||||||
|                 offset = 0; |  | ||||||
|             } |  | ||||||
|             dtype = (data.slice(offset, offset + 1)).toLowerCase(); |  | ||||||
| 
 |  | ||||||
|             dataoffset = offset + 2; |  | ||||||
| 
 |  | ||||||
|             switch (dtype) { |  | ||||||
|                 case 'i': |  | ||||||
|                     typeconvert = (x: any): number => { |  | ||||||
|                         return parseInt(x, 10); |  | ||||||
|                     }; |  | ||||||
|                     readData = readUntil(data, dataoffset, ';'); |  | ||||||
|                     chrs = readData[0]; |  | ||||||
|                     readdata = readData[1]; |  | ||||||
|                     dataoffset += chrs + 1; |  | ||||||
|                     break; |  | ||||||
|                 case 'b': |  | ||||||
|                     typeconvert = (x: any): boolean => { |  | ||||||
|                         return parseInt(x, 10) !== 0; |  | ||||||
|                     }; |  | ||||||
|                     readData = readUntil(data, dataoffset, ';'); |  | ||||||
|                     chrs = readData[0]; |  | ||||||
|                     readdata = readData[1]; |  | ||||||
|                     dataoffset += chrs + 1; |  | ||||||
|                     break; |  | ||||||
|                 case 'd': |  | ||||||
|                     typeconvert = (x: any): number => { |  | ||||||
|                         return parseFloat(x); |  | ||||||
|                     }; |  | ||||||
|                     readData = readUntil(data, dataoffset, ';'); |  | ||||||
|                     chrs = readData[0]; |  | ||||||
|                     readdata = readData[1]; |  | ||||||
|                     dataoffset += chrs + 1; |  | ||||||
|                     break; |  | ||||||
|                 case 'n': |  | ||||||
|                     readdata = null; |  | ||||||
|                     break; |  | ||||||
|                 case 's': |  | ||||||
|                     ccount = readUntil(data, dataoffset, ':'); |  | ||||||
|                     chrs = ccount[0]; |  | ||||||
|                     stringlength = ccount[1]; |  | ||||||
|                     dataoffset += chrs + 2; |  | ||||||
| 
 |  | ||||||
|                     readData = readChrs(data, dataoffset + 1, parseInt(stringlength, 10)); |  | ||||||
|                     chrs = readData[0]; |  | ||||||
|                     readdata = readData[1]; |  | ||||||
|                     dataoffset += chrs + 2; |  | ||||||
|                     if (chrs !== parseInt(stringlength, 10) && chrs !== readdata.length) { |  | ||||||
|                         error('SyntaxError', 'String length mismatch'); |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 case 'a': |  | ||||||
|                     readdata = {}; |  | ||||||
| 
 |  | ||||||
|                     keyandchrs = readUntil(data, dataoffset, ':'); |  | ||||||
|                     chrs = keyandchrs[0]; |  | ||||||
|                     keys = keyandchrs[1]; |  | ||||||
|                     dataoffset += chrs + 2; |  | ||||||
| 
 |  | ||||||
|                     length = parseInt(keys, 10); |  | ||||||
|                     contig = true; |  | ||||||
| 
 |  | ||||||
|                     for (let i = 0; i < length; i++) { |  | ||||||
|                         kprops = _unserialize(data, dataoffset); |  | ||||||
|                         kchrs = kprops[1]; |  | ||||||
|                         key = kprops[2]; |  | ||||||
|                         dataoffset += kchrs; |  | ||||||
| 
 |  | ||||||
|                         vprops = _unserialize(data, dataoffset); |  | ||||||
|                         vchrs = vprops[1]; |  | ||||||
|                         value = vprops[2]; |  | ||||||
|                         dataoffset += vchrs; |  | ||||||
| 
 |  | ||||||
|                         if (key !== i) { |  | ||||||
|                             contig = false; |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         readdata[key] = value; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (contig) { |  | ||||||
|                         array = new Array(length); |  | ||||||
|                         for (i = 0; i < length; i++) { |  | ||||||
|                             array[i] = readdata[i]; |  | ||||||
|                         } |  | ||||||
|                         readdata = array; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     dataoffset += 1; |  | ||||||
|                     break; |  | ||||||
|                 default: |  | ||||||
|                     error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype); |  | ||||||
|                     break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return [dtype, dataoffset - offset, typeconvert(readdata)]; |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         return _unserialize((data + ''), 0)[2]; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -1138,16 +917,13 @@ export class CoreTextUtilsProvider { | |||||||
|      * @param title Title of the new state. |      * @param title Title of the new state. | ||||||
|      * @param text Content of the text to be expanded. |      * @param text Content of the text to be expanded. | ||||||
|      * @param component Component to link the embedded files to. |      * @param component Component to link the embedded files to. | ||||||
|      * @param componentId An ID to use in conjunction with the component. |      * @param options Options. | ||||||
|      * @param files List of files to display along with the text. |  | ||||||
|      * @param filter Whether the text should be filtered. |  | ||||||
|      * @param contextLevel The context level. |  | ||||||
|      * @param instanceId The instance ID related to the context. |  | ||||||
|      * @param courseId Course ID the text belongs to. It can be used to improve performance with filters. |  | ||||||
|      */ |      */ | ||||||
|  |     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||||
|     viewText(title: string, text: string, options?: CoreTextUtilsViewTextOptions): void { |     viewText(title: string, text: string, options?: CoreTextUtilsViewTextOptions): void { | ||||||
|         // @todo
 |         // @todo
 | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -1156,7 +932,7 @@ export class CoreTextUtilsProvider { | |||||||
| export type CoreTextUtilsViewTextOptions = { | export type CoreTextUtilsViewTextOptions = { | ||||||
|     component?: string; // Component to link the embedded files to.
 |     component?: string; // Component to link the embedded files to.
 | ||||||
|     componentId?: string | number; // An ID to use in conjunction with the component.
 |     componentId?: string | number; // An ID to use in conjunction with the component.
 | ||||||
|     files?: any[]; // List of files to display along with the text.
 |     files?: CoreWSExternalFile[]; // List of files to display along with the text.
 | ||||||
|     filter?: boolean; // Whether the text should be filtered.
 |     filter?: boolean; // Whether the text should be filtered.
 | ||||||
|     contextLevel?: string; // The context level.
 |     contextLevel?: string; // The context level.
 | ||||||
|     instanceId?: number; // The instance ID related to the context.
 |     instanceId?: number; // The instance ID related to the context.
 | ||||||
|  | |||||||
							
								
								
									
										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); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | }; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user