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.
- package/LICENSE +191 -0
- package/README.md +41 -0
- package/dist/main.cjs +3785 -0
- package/dist/main.d.cts +316 -0
- package/dist/main.d.ts +316 -0
- package/dist/main.mjs +3681 -0
- package/package.json +103 -0
- package/src/base64Image.ts +101 -0
- package/src/cssParser.ts +59 -0
- package/src/filters.ts +226 -0
- package/src/font-utils.ts +204 -0
- package/src/get-default-fields-value.ts +64 -0
- package/src/getSvg.ts +284 -0
- package/src/imageHelper.ts +293 -0
- package/src/imagePassportUtils.ts +718 -0
- package/src/images-processor.ts +186 -0
- package/src/main.ts +1354 -0
- package/src/polyfills/DOMParser.ts +28 -0
- package/src/polyfills/File.ts +20 -0
- package/src/polyfills/Image.ts +90 -0
- package/src/svgScaler.ts +179 -0
- package/src/textGenCodeParser.ts +319 -0
- package/src/time.ts +147 -0
- package/src/toolsFunc.ts +79 -0
- package/src/types.ts +177 -0
- package/src/utils.ts +758 -0
- package/src/watermaker.ts +190 -0
|
@@ -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
|
+
}
|
package/src/svgScaler.ts
ADDED
|
@@ -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
|
+
}
|