frontbacked-svg 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+ const isNode = typeof window === 'undefined';
2
+
3
+ export class DOMParserWrapper {
4
+ private parser: any;
5
+
6
+ constructor() {
7
+ if (isNode) {
8
+ const { JSDOM } = require("jsdom");
9
+ this.parser = JSDOM;
10
+ } else {
11
+ this.parser = new DOMParser();
12
+ }
13
+ }
14
+
15
+ public parseFromString(s: string, contentType = 'text/html'): any {
16
+ if (isNode) {
17
+ const dom = new this.parser(s, { contentType });
18
+ return dom.window.document;
19
+ } else {
20
+ return this.parser.parseFromString(s, contentType);
21
+ }
22
+ }
23
+ }
24
+
25
+ // Register the global DOMParser in Node.js
26
+ if (isNode) {
27
+ (globalThis as any).DOMParser = DOMParserWrapper;
28
+ }
@@ -0,0 +1,20 @@
1
+
2
+ const isNode = typeof window === 'undefined';
3
+
4
+ export class FileWrapper {
5
+
6
+ constructor(blob: any, name: string, mime: any) {
7
+ if(!isNode) {
8
+ return new File(blob, name, mime)
9
+
10
+ } else {
11
+ return blob
12
+ }
13
+ }
14
+
15
+ }
16
+
17
+ // Register the global Image in Node.js
18
+ if (isNode) {
19
+ (globalThis as any).File = FileWrapper;
20
+ }
@@ -0,0 +1,90 @@
1
+ const isNode = typeof window === 'undefined';
2
+
3
+ export class ImageWrapper {
4
+ private _onload: (() => void) | null = null;
5
+ private _onerror: ((err: any) => void) | null = null;
6
+ private _src: string = '';
7
+ private _imageLoaded: boolean = false;
8
+ private _realImage: any;
9
+
10
+ constructor() {
11
+ if (isNode) {
12
+ const { Image } = require('canvas');
13
+ this._realImage = new Image();
14
+ } else {
15
+ this._realImage = new window.Image();
16
+ }
17
+ }
18
+
19
+ get onload() {
20
+ return this._onload;
21
+ }
22
+
23
+ set onload(callback: (() => void) | null) {
24
+ this._onload = callback;
25
+ this._realImage.onload = callback;
26
+ }
27
+
28
+ get onerror() {
29
+ return this._onerror;
30
+ }
31
+
32
+ set onerror(callback: ((err: any) => void) | null) {
33
+ this._onerror = callback;
34
+ this._realImage.onerror = callback;
35
+ }
36
+
37
+ get realImage(): any {
38
+ return this._realImage;
39
+ }
40
+
41
+ get src(): string {
42
+ return this._src;
43
+ }
44
+
45
+ set src(value: string) {
46
+ this._src = value;
47
+ this._imageLoaded = false;
48
+
49
+ if (isNode) {
50
+ const { loadImage } = require('canvas');
51
+
52
+ const handleLoad = (img: any) => {
53
+ this._realImage = img; // Replace _realImage with the fully loaded image
54
+ this._imageLoaded = true;
55
+ if (this._onload) this._onload();
56
+ };
57
+
58
+ const handleError = (err: any) => {
59
+ this._imageLoaded = false;
60
+ if (this._onerror) this._onerror(err);
61
+ };
62
+
63
+ if (value.startsWith('data:image/') || value.startsWith('data:img/')) {
64
+ const buffer = Buffer.from(value.split(',')[1], 'base64');
65
+ loadImage(buffer).then(handleLoad).catch(handleError);
66
+ } else {
67
+ loadImage(value).then(handleLoad).catch(handleError);
68
+ }
69
+ } else {
70
+ this._realImage.src = value; // Native browser behavior
71
+ }
72
+ }
73
+
74
+ get width(): number {
75
+ return this._realImage.width;
76
+ }
77
+
78
+ get height(): number {
79
+ return this._realImage.height;
80
+ }
81
+
82
+ get complete(): boolean {
83
+ return this._imageLoaded;
84
+ }
85
+ }
86
+
87
+ // Register the global Image in Node.js
88
+ if (isNode) {
89
+ (globalThis as any).Image = ImageWrapper;
90
+ }
@@ -0,0 +1,179 @@
1
+
2
+ const scaleDownFont = (scaleH: number, scaleV: number, maxChars: number, chars: number) => {
3
+ // If the number of characters is within the maximum allowed,
4
+ // return the original scales.
5
+ if (chars <= maxChars) {
6
+ return { scaleH, scaleV };
7
+ }
8
+
9
+ // Calculate the reduction factor.
10
+ const factor = maxChars / chars;
11
+
12
+ // Apply the reduction factor to both horizontal and vertical scales.
13
+ return {
14
+ scaleH: scaleH * factor,
15
+ scaleV: scaleV * factor
16
+ };
17
+ }
18
+
19
+ export function responsiveTransform(
20
+ transform: string,
21
+ widthScaler: (value: number) => number,
22
+ heightScaler: (value: number) => number,
23
+ textContent?: string | null,
24
+ maxTextBeforeScaleDown?: number | number
25
+ ) {
26
+ // Regex pattern to match different types of transforms, including matrix
27
+ const transformRegex = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]+)\)/g;
28
+
29
+ let newTransform = transform.replace(transformRegex, (match, type, values) => {
30
+ // Split the values inside the parentheses by space or comma
31
+ let valueArray = values.split(/[ ,]+/).map(Number);
32
+
33
+ // Adjust the values depending on the type of transform
34
+ if (type === 'translate') {
35
+ // Apply the widthScaler to the x value and the heightScaler to the y value
36
+ valueArray[0] = widthScaler(valueArray[0]); // X
37
+ valueArray[1] = heightScaler(valueArray[1]); // Y
38
+
39
+ } else if (type === 'scale') {
40
+ // Apply the widthScaler to the first value and the heightScaler to the second if available
41
+ //No need to scale since we're already scaling the font-size
42
+ valueArray[0] = valueArray[0]//widthScaler(valueArray[0]); // X scaling
43
+ if (valueArray.length > 1) {
44
+ //No need to scale since where's already scaling the font-size
45
+ valueArray[1] = valueArray[1]//heightScaler(valueArray[1]); // Y scaling
46
+ } else {
47
+ // If there is only one scale value, assume uniform scaling
48
+ valueArray[1] = valueArray[0];
49
+ }
50
+
51
+ if(maxTextBeforeScaleDown) {
52
+ const { scaleH, scaleV } = scaleDownFont(valueArray[0], valueArray[1], maxTextBeforeScaleDown, (textContent || "").length)
53
+ if([1.499].includes(valueArray[1])) {
54
+ //console.log("valueArray:", valueArray, scaleH, scaleV, maxTextBeforeScaleDown, textContent)
55
+ }
56
+ valueArray[0] = scaleH
57
+ valueArray[1] = scaleV
58
+ }
59
+
60
+ } else if (type === 'matrix') {
61
+ // Apply the widthScaler and heightScaler to the matrix values
62
+ //valueArray[0] = valueArray[0]; // a (x-scale)
63
+ //valueArray[1] = valueArray[1]; // b (y-skew)
64
+ //valueArray[2] = valueArray[2]; // c (x-skew)
65
+ //valueArray[3] = valueArray[3]; // d (y-scale)
66
+ valueArray[4] = widthScaler(valueArray[4]); // e (x-translation)
67
+ valueArray[5] = heightScaler(valueArray[5]); // f (y-translation)
68
+
69
+ } else if (type === 'rotate') {
70
+ // No scaling needed for rotate values
71
+ // Just return the rotate transform unchanged
72
+ return `${type}(${values})`;
73
+
74
+ } else if (type === 'skewX') {
75
+ // No scaling needed for rotate values
76
+ // Just return the rotate transform unchanged
77
+ return `${type}(${widthScaler(Number(values.trim()))})`;
78
+
79
+ } else if (type === 'skewY') {
80
+ // No scaling needed for rotate values
81
+ // Just return the rotate transform unchanged
82
+ return `${type}(${heightScaler(Number(values.trim()))})`;
83
+ }
84
+
85
+ // Return the transformed string for this particular transform
86
+ return `${type}(${valueArray.join(' ')})`;
87
+ });
88
+
89
+ return newTransform;
90
+ }
91
+
92
+ export function responsivePathD(d: string, widthScaler: (value: number) => number, heightScaler: (value: number) => number) {
93
+ // Regex pattern to match path commands and their values
94
+ const commandRegex = /([MLHVCSQTZ])([^MLHVCSQTZ]*)/gi;
95
+
96
+ let newD = d.replace(commandRegex, (match, command, values) => {
97
+ // Split the values by commas or spaces but retain commas in the result
98
+ let valueArray = values.trim().split(/([, ])/).map((v: string) => v.trim());
99
+
100
+ // Process numeric values only
101
+ for (let i = 0; i < valueArray.length; i++) {
102
+ // Parse numeric values and apply scaling; leave commas and spaces unchanged
103
+ if (!isNaN(parseFloat(valueArray[i]))) {
104
+ if (command.toUpperCase() === 'H') {
105
+ valueArray[i] = widthScaler(parseFloat(valueArray[i])); // scale x for H
106
+ } else if (command.toUpperCase() === 'V') {
107
+ valueArray[i] = heightScaler(parseFloat(valueArray[i])); // scale y for V
108
+ } else {
109
+ // Scale both x and y for M, L, C, etc.
110
+ if (i % 2 === 0) {
111
+ valueArray[i] = widthScaler(parseFloat(valueArray[i])); // x values
112
+ } else {
113
+ valueArray[i] = heightScaler(parseFloat(valueArray[i])); // y values
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ // Rejoin the values, preserving commas
120
+ return `${command}${valueArray.join('')}`;
121
+ });
122
+
123
+ return newD;
124
+ }
125
+
126
+ export function responsiveViewBox(viewBox: string, widthScaler: (value: number) => number, heightScaler: (value: number) => number) {
127
+ // Split the viewBox values (min-x, min-y, width, height) by spaces
128
+ let viewBoxValues = viewBox.trim().split(' ').map((v: string) => parseFloat(v));
129
+
130
+ if (viewBoxValues.length !== 4) {
131
+ throw new Error("Invalid viewBox format. Expected format: 'min-x min-y width height'");
132
+ }
133
+
134
+ // Scale the values
135
+ viewBoxValues[0] = widthScaler(viewBoxValues[0]); // min-x
136
+ viewBoxValues[1] = heightScaler(viewBoxValues[1]); // min-y
137
+ viewBoxValues[2] = widthScaler(viewBoxValues[2]); // width
138
+ viewBoxValues[3] = heightScaler(viewBoxValues[3]); // height
139
+
140
+ // Return the transformed viewBox as a string
141
+ return viewBoxValues.join(' ');
142
+ }
143
+
144
+ export function responsiveFontSize(fontSize: string, originalWidth: number, originalHeight: number, targetWidth: number, targetHeight: number) {
145
+ // Calculate the scaling factor using either width or height
146
+ const widthScaleFactor = targetWidth / originalWidth;
147
+ const heightScaleFactor = targetHeight / originalHeight;
148
+
149
+ // You can choose one scaling method, e.g., height or width, or use the average of both
150
+ const scalingFactor = Math.min(widthScaleFactor, heightScaleFactor); // Use whichever is smaller for better readability
151
+
152
+ // Regex to extract numeric part and unit (if any)
153
+ const fontSizeRegex = /^(\d+(\.\d+)?)([a-z%]*)$/i;
154
+ const match = fontSize.match(fontSizeRegex);
155
+
156
+ if (!match) {
157
+ throw new Error("Invalid font size format");
158
+ }
159
+
160
+ const numericValue = parseFloat(match[1]); // The numeric part
161
+ const unit = match[3] || ""; // The unit (e.g., px), default to empty if no unit
162
+
163
+ // Scale the numeric part
164
+ const scaledValue = numericValue * scalingFactor;
165
+
166
+ // Return the scaled font size with the original unit
167
+ return `${scaledValue}${unit}`;
168
+ }
169
+
170
+ //matrix(-0.08, -0.997, 0.998, -0.08, 4559.744, 2798.756)
171
+
172
+ //matrix(-0.028 -0.354 0.355 -0.028 1620.963 994.942)
173
+
174
+ //matrix(a, b, c, d, e, f)
175
+ //matrix(a, skewX, skewY, d, translateX, translateY)
176
+
177
+ //matrix(-0.028 -0.354 0.355 -0.028 1620.963 994.942)
178
+
179
+ //matrix(-0.08, -0.997, 0.998, -0.08, 1620.963 994.942)
@@ -0,0 +1,319 @@
1
+ const timestampToDate = (timestamp: any | Date | null | undefined): Date => {
2
+ try {
3
+ return (timestamp as any).toDate()
4
+
5
+ } catch(e) {
6
+ return timestamp as Date
7
+ }
8
+ }
9
+
10
+ class SeededRandom {
11
+ seed: number;
12
+
13
+ constructor(seed: number) {
14
+ this.seed = seed;
15
+ }
16
+
17
+ random(): number {
18
+ const x = Math.sin(this.seed++) * 10000;
19
+ return x - Math.floor(x);
20
+ }
21
+
22
+ randomInt(min: number, max: number): number {
23
+ return Math.floor(this.random() * (max - min + 1)) + min;
24
+ }
25
+ }
26
+
27
+ // Function to create a seed based on code, relevant data values, and position
28
+ function createSeed(code?: string | null, position?: number | null, data?: { [x: string]: any } | null, conditionFn?: ((key: string) => boolean) | null): number {
29
+ if(
30
+ code === null || code === undefined ||
31
+ data === null || data === undefined ||
32
+ position === null || position === undefined ||
33
+ conditionFn === null || conditionFn === undefined
34
+ ) return 0
35
+ const relevantData = Object.keys(data)
36
+ .filter(key => conditionFn(key))
37
+ .map(key => data[key].toString().replace(/\s+/g, ''))
38
+ .join('');
39
+ const seedString = (code + relevantData + position.toString()).toLowerCase();
40
+ let seed = 0;
41
+ for (let i = 0; i < seedString.length; i++) {
42
+ seed = (seed << 5) - seed + seedString.charCodeAt(i);
43
+ seed |= 0; // Convert to 32-bit integer
44
+ }
45
+ return seed;
46
+ }
47
+
48
+ function formatDate(date: Date, format: string): string {
49
+ //console.log("formatDate:", date, "format", format)
50
+ try {
51
+ //console.log("formatDate:2", date, "format", format, date.getFullYear().toString())
52
+
53
+ } catch(e: any) {
54
+ //console.log("formatDate:error", date, "format", format, "error", e.message)
55
+ }
56
+
57
+ const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
58
+ const monthNames = [
59
+ 'January', 'February', 'March', 'April', 'May', 'June',
60
+ 'July', 'August', 'September', 'October', 'November', 'December'
61
+ ];
62
+
63
+ const replacements: {[x: string]: string} = {
64
+ YYYY: date.getFullYear().toString(), // Full year
65
+ YY: String(date.getFullYear()).slice(-2), // Last two digits of year
66
+ MMMM: monthNames[date.getMonth()], // Full month name
67
+ MMM: monthNames[date.getMonth()].slice(0, 3), // Abbreviated month name
68
+ MM: String(date.getMonth() + 1).padStart(2, '0'), // Month number with leading zero
69
+ M: (date.getMonth() + 1).toString(), // Month number without leading zero
70
+ DD: String(date.getDate()).padStart(2, '0'), // Day of the month with leading zero
71
+ D: date.getDate().toString(), // Day of the month without leading zero
72
+ dddd: dayNames[date.getDay()], // Full day of the week name
73
+ ddd: dayNames[date.getDay()].slice(0, 3), // Abbreviated day of the week name
74
+ HH: String(date.getHours()).padStart(2, '0'), // Hour (24-hour format) with leading zero
75
+ H: date.getHours().toString(), // Hour (24-hour format) without leading zero
76
+ hh: String((date.getHours() % 12) || 12).padStart(2, '0'), // Hour (12-hour format) with leading zero
77
+ h: ((date.getHours() % 12) || 12).toString(), // Hour (12-hour format) without leading zero
78
+ mm: String(date.getMinutes()).padStart(2, '0'), // Minutes with leading zero
79
+ m: date.getMinutes().toString(), // Minutes without leading zero
80
+ ss: String(date.getSeconds()).padStart(2, '0'), // Seconds with leading zero
81
+ s: date.getSeconds().toString(), // Seconds without leading zero
82
+ A: date.getHours() < 12 ? 'AM' : 'PM', // AM or PM
83
+ a: date.getHours() < 12 ? 'am' : 'pm' // am or pm
84
+ };
85
+
86
+ // Handle escape characters
87
+ return replacements[format] || "";
88
+ }
89
+
90
+ // Example usage:
91
+ //const today = new Date();
92
+ //const formatString = "Hello James, today is \\YYYY/MM/DD, and it's \\dddd! Current time is HH:mm:ss \\(\\AM/PM\\).";
93
+ //console.log(formatDate(today, formatString));
94
+ // Output example: "Hello James, today is YYYY/MM/DD, and it's dddd! Current time is 14:25:09 (AM/PM)."
95
+
96
+ export function textGenCodeParser(code: string | null, data: { [x: string]: any } | null, conditionFn?: (key: string) => boolean, onData?: (dataKey: string, data: string) => string) {
97
+ if (!code || !data) return "";
98
+
99
+ // Helper function to get words from a string
100
+ function getWords(str: string) {
101
+ return str.split(' ');
102
+ }
103
+
104
+ // Function to generate a random number based on seed
105
+ function randomNumber(min: string, max: string, position: number) {
106
+ const seed = conditionFn ? createSeed(code, position, data, conditionFn) : Math.random() * 1000000;
107
+ const seededRandom = new SeededRandom(seed);
108
+
109
+ // Check if min and max are numbers or number of digits
110
+ const isDigits = (value: any) => typeof value === 'string' && value.endsWith('d');
111
+ const isNumber = (value: any) => !isNaN(value) && !isDigits(`${value}`);
112
+
113
+ if (isNumber(min) && isNumber(max)) {
114
+ // Generate random number within the specified range
115
+ return seededRandom.randomInt(parseInt(min), parseInt(max)).toString();
116
+ } else if (isDigits(min) && isDigits(max)) {
117
+ // Parse number of digits from min and max
118
+ const minDigits = parseInt(min);
119
+ const maxDigits = parseInt(max);
120
+
121
+ // Generate a random number with a random number of digits between minDigits and maxDigits
122
+ let result = '';
123
+ let currentDigits = 0;
124
+
125
+ const numOfDigits = seededRandom.randomInt(minDigits, maxDigits);
126
+
127
+ while (currentDigits < numOfDigits) {
128
+ const num = seededRandom.randomInt(1, 9); // Ensure the first digit is not zero
129
+ result += num.toString();
130
+ currentDigits = result.length;
131
+ }
132
+
133
+ return result;
134
+ } else if (isDigits(min)) {
135
+ // Generate a number with exactly min digits
136
+ const minDigits = parseInt(min);
137
+ let result = seededRandom.randomInt(1, 9).toString(); // Ensure the first digit is not zero
138
+
139
+ for (let i = 1; i < minDigits; i++) {
140
+ const num = seededRandom.randomInt(0, 9); // Include zero as a possible digit
141
+ result += num.toString();
142
+ }
143
+
144
+ return result;
145
+ } else if (isNumber(min)) {
146
+ return Math.round(seededRandom.random() * parseInt(min)).toString();
147
+ } else {
148
+ // Invalid input scenario
149
+ return 'Invalid arguments for randomNumber function: ' + `Min: ${min} - ${typeof min} | Max: ${max} - ${typeof max}`;
150
+ }
151
+ }
152
+
153
+ // Helper function to handle variable substitution
154
+ function substituteVariable(key: string) {
155
+ if (!data) return '';
156
+ key = key.replace(/[{}]+/g, "");
157
+ // Check if the variable has a word slice
158
+ const wordSliceMatch = key.match(/(.+)\[w(\d+)\]/);
159
+ if (wordSliceMatch) {
160
+ const [, baseKey, wordIndex] = wordSliceMatch;
161
+ const d = onData? onData(baseKey, data[baseKey] || '') : data[baseKey]
162
+ const words = getWords(d || '');
163
+
164
+ //if(key.includes("id")) console.log("textGenCodeParser.match.l.d", key, baseKey, data[baseKey])
165
+ return words[parseInt(wordIndex) - 1] || '';
166
+
167
+ } else if (key in data) {
168
+ //if(key.includes("id")) console.log("textGenCodeParser.match.l.e", key, data[key])
169
+ const d = onData? onData(key, data[key] || '') : data[key]
170
+ //if(key.includes("id")) console.log("textGenCodeParser.match.l.f", d)
171
+ return d;
172
+ }
173
+ return '';
174
+ }
175
+
176
+ function extractVariables(code: string) {
177
+ const regex = /{([^{}]+?)}(\.\w+\[(.*?)\])?/g;
178
+ const results = [];
179
+ let match;
180
+ //console.log("textGenCodeParser.match.1", match, "code", code)
181
+ while ((match = regex.exec(code)) !== null) {
182
+ //if(code.includes("id")) console.log("textGenCodeParser.match.l.a", match, "code", code)
183
+ const varKey = match[1];
184
+ const varFunc = match[2] ? match[2].split('[')[0].slice(1) : null;
185
+ const varFuncArgs = match[3] ? match[3].split(',').map(arg => arg.trim()) : [];
186
+
187
+ const replacementStart = match.index;
188
+ const replacementEnd = match.index + match[0].length - 1;
189
+
190
+ const extracts = {
191
+ varKey,
192
+ varFunc: varFunc || undefined,
193
+ varFuncArgs: varFuncArgs.length ? varFuncArgs : undefined,
194
+ replacementStart,
195
+ replacementEnd
196
+ }
197
+
198
+ //if(code.includes("id")) console.log("textGenCodeParser.match.l.b", match, "code", code, "extracts", extracts)
199
+
200
+ results.push(extracts);
201
+ }
202
+
203
+ return results;
204
+ }
205
+
206
+ function replaceSubtext(text: string, replacementText: string, start: number, end: number) {
207
+ // Extract the parts
208
+ const before = text.slice(0, start);
209
+ const after = text.slice(end + 1);
210
+
211
+ // Construct the new text
212
+ return before + replacementText + after;
213
+ }
214
+
215
+ function cut(variable: any, varA: string, varB?: string) {
216
+ var var1 = parseInt(varA)
217
+ var var2 = parseInt(varB || "")
218
+ return isNaN(var2)? variable.substring(var1 - 1) : variable.substring(var1 - 1, var2)
219
+ }
220
+
221
+ function callVarFunc (variable: any, varFunc: string, varFuncArgs: string[] | undefined) {
222
+ var result = null
223
+ switch (varFunc) {
224
+ case "cut":
225
+ if(varFuncArgs && [1, 2].includes(varFuncArgs.length)) {
226
+ try {
227
+
228
+ result = cut(variable, varFuncArgs[0], varFuncArgs[1])
229
+
230
+ } catch(e) {}
231
+ }
232
+ break;
233
+ case "df":
234
+ if(varFuncArgs && [1,2,3].includes(varFuncArgs.length)) {
235
+ try {
236
+ var format = varFuncArgs[0]
237
+ var date = variable == "_"? new Date() : timestampToDate(variable)
238
+
239
+ result = formatDate(date, format)
240
+ if(varFuncArgs.length > 1) {
241
+ result = cut(result, varFuncArgs[1], varFuncArgs[2])
242
+ }
243
+
244
+ } catch(e) {console.log(e)}
245
+ }
246
+ break;
247
+
248
+ default:
249
+ break;
250
+ }
251
+
252
+ return result
253
+
254
+ }
255
+
256
+ // Replace variables and handle special syntax
257
+ const repList: { id: string, char: string, count: number }[] = [];
258
+ let positionCounter = 0;
259
+ let intermediateResult = code.replace(/\(([^()]*)\)/g, (match, p1) => {
260
+ // Check for special syntax within parentheses
261
+ if (p1.startsWith("rn[")) {
262
+ // Random number generation
263
+ const [min, max] = p1.slice(3, -1).split(',');
264
+ return randomNumber(min, max, positionCounter++);
265
+ } else if (p1.match(/^([^*]+)(\*{1,2})(\d+)$/)) {
266
+ // Fixed count repetition
267
+ const [, char, operand, count] = p1.match(/^([^*]+)(\*{1,2})(\d+)$/) || [];
268
+ const total = parseInt(count);
269
+ // Repeat until the chars is the number of count
270
+ if (operand == "*") {
271
+ return char.repeat(total);
272
+ } // Repeat until the whole generated text length is the number of count
273
+ else {
274
+ const repId = `<${Math.floor(Math.random() * 1000000000)}>`;
275
+ repList.push({ id: repId, char, count: total });
276
+ return repId;
277
+ }
278
+ } else if (p1.includes("{") && p1.includes("}")) {
279
+ // Variable substitution
280
+ //'abc{var}ghi', 'abc{var}', '{var}'
281
+ var p1CodeExtraction = extractVariables(p1)
282
+
283
+ // Sort the extractions in reverse order of `replacementStart` to avoid position shifting during replacements
284
+ p1CodeExtraction.sort((a, b) => b.replacementStart - a.replacementStart);
285
+
286
+ //if(code.includes("id")) console.log("textGenCodeParser.match.l.c", match, "code", code, p1CodeExtraction)
287
+
288
+ for(const extraction of p1CodeExtraction) {
289
+ var variable = substituteVariable(extraction.varKey)
290
+ if(extraction.varFunc) {
291
+ variable = callVarFunc(variable, extraction.varFunc, extraction.varFuncArgs)
292
+ }
293
+
294
+ if(variable && p1.length > 0) {
295
+ p1 = replaceSubtext(p1, variable, extraction.replacementStart, extraction.replacementEnd)
296
+
297
+ } else {
298
+ p1 = ""
299
+ break
300
+ }
301
+ }
302
+
303
+ return p1;
304
+ } else {
305
+ return p1;
306
+ }
307
+ });
308
+
309
+ for (const rep of repList) {
310
+ const textLength = (intermediateResult.length - rep.id.length);
311
+ if (textLength >= rep.count) {
312
+ break;
313
+ }
314
+ const reps = rep.char.repeat(rep.count - textLength);
315
+ intermediateResult = intermediateResult.replace(rep.id, reps);
316
+ }
317
+
318
+ return intermediateResult;
319
+ }