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 { 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,8 @@ 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[], | ||||
|     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 +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. | ||||
|      * @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 +610,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 +632,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 +679,7 @@ export class CoreTextUtilsProvider { | ||||
|             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. | ||||
|      * @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 +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. | ||||
|      * @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 +808,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 +817,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 +855,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 +903,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 +917,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 +932,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.
 | ||||
|  | ||||
							
								
								
									
										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