MOBILE-3320 lint: Fix linting in text service
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.
|
||||
|
|
|
@ -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…
Reference in New Issue