create-prisma-php-app 1.14.2 → 1.15.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/dist/bootstrap.php +54 -36
- package/dist/index.js +1 -1
- package/dist/prisma-client-php/index.enc +1 -1
- package/dist/src/Lib/Prisma/Classes/Utility.php +4 -4
- package/dist/src/Lib/Validator.php +158 -40
- package/dist/src/app/js/index.js +1 -342
- package/package.json +1 -1
|
@@ -236,7 +236,7 @@ abstract class Utility
|
|
|
236
236
|
case 'endsWith':
|
|
237
237
|
case 'equals':
|
|
238
238
|
case 'not':
|
|
239
|
-
$validatedValue = Validator::
|
|
239
|
+
$validatedValue = Validator::string($val);
|
|
240
240
|
$likeOperator = $condition === 'contains' ? ($dbType == 'pgsql' ? 'ILIKE' : 'LIKE') : '=';
|
|
241
241
|
if ($condition === 'startsWith') $validatedValue .= '%';
|
|
242
242
|
if ($condition === 'endsWith') $validatedValue = '%' . $validatedValue;
|
|
@@ -248,7 +248,7 @@ abstract class Utility
|
|
|
248
248
|
case 'gte':
|
|
249
249
|
case 'lt':
|
|
250
250
|
case 'lte':
|
|
251
|
-
$validatedValue = is_float($val) ? Validator::
|
|
251
|
+
$validatedValue = is_float($val) ? Validator::float($val) : Validator::int($val);
|
|
252
252
|
$operator = $condition === 'gt' ? '>' : ($condition === 'gte' ? '>=' : ($condition === 'lt' ? '<' : '<='));
|
|
253
253
|
$sqlConditions[] = "$fieldQuoted $operator $bindingKey";
|
|
254
254
|
$bindings[$bindingKey] = $validatedValue;
|
|
@@ -259,7 +259,7 @@ abstract class Utility
|
|
|
259
259
|
foreach ($val as $i => $inVal) {
|
|
260
260
|
$inKey = $bindingKey . "_" . $i;
|
|
261
261
|
// Assuming the values are strings; adjust validation as necessary.
|
|
262
|
-
$validatedValue = Validator::
|
|
262
|
+
$validatedValue = Validator::string($inVal);
|
|
263
263
|
$inPlaceholders[] = $inKey;
|
|
264
264
|
$bindings[$inKey] = $validatedValue;
|
|
265
265
|
}
|
|
@@ -276,7 +276,7 @@ abstract class Utility
|
|
|
276
276
|
// For scalar non-array values; direct equals condition assumed
|
|
277
277
|
$bindingKey = ":" . $prefix . $key . $level;
|
|
278
278
|
// Use appropriate validation based on expected type; assuming string for simplicity
|
|
279
|
-
$validatedValue = Validator::
|
|
279
|
+
$validatedValue = Validator::string($value);
|
|
280
280
|
$sqlConditions[] = "$fieldQuoted = $bindingKey";
|
|
281
281
|
$bindings[$bindingKey] = $validatedValue;
|
|
282
282
|
}
|
|
@@ -4,76 +4,194 @@ namespace Lib;
|
|
|
4
4
|
|
|
5
5
|
class Validator
|
|
6
6
|
{
|
|
7
|
-
|
|
7
|
+
// String Validation
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Validate and sanitize a string.
|
|
11
|
+
*
|
|
12
|
+
* @param mixed $value The value to validate.
|
|
13
|
+
* @return string The sanitized string.
|
|
14
|
+
*/
|
|
15
|
+
public static function string($value): string
|
|
8
16
|
{
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
17
|
+
return $value !== null ? htmlspecialchars(trim($value), ENT_QUOTES, 'UTF-8') : '';
|
|
18
|
+
}
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Validate an email address.
|
|
22
|
+
*
|
|
23
|
+
* @param mixed $value The value to validate.
|
|
24
|
+
* @return string|null The valid email address or null if invalid.
|
|
25
|
+
*/
|
|
26
|
+
public static function email($value): ?string
|
|
27
|
+
{
|
|
28
|
+
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false ? $value : null;
|
|
14
29
|
}
|
|
15
30
|
|
|
16
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Validate a URL.
|
|
33
|
+
*
|
|
34
|
+
* @param mixed $value The value to validate.
|
|
35
|
+
* @return string|null The valid URL or null if invalid.
|
|
36
|
+
*/
|
|
37
|
+
public static function url($value): ?string
|
|
17
38
|
{
|
|
18
|
-
return filter_var($value,
|
|
39
|
+
return filter_var($value, FILTER_VALIDATE_URL) !== false ? $value : null;
|
|
19
40
|
}
|
|
20
41
|
|
|
21
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Validate an IP address.
|
|
44
|
+
*
|
|
45
|
+
* @param mixed $value The value to validate.
|
|
46
|
+
* @return string|null The valid IP address or null if invalid.
|
|
47
|
+
*/
|
|
48
|
+
public static function ip($value): ?string
|
|
22
49
|
{
|
|
23
|
-
return filter_var($value,
|
|
50
|
+
return filter_var($value, FILTER_VALIDATE_IP) !== false ? $value : null;
|
|
24
51
|
}
|
|
25
52
|
|
|
26
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Validate a UUID.
|
|
55
|
+
*
|
|
56
|
+
* @param mixed $value The value to validate.
|
|
57
|
+
* @return string|null The valid UUID or null if invalid.
|
|
58
|
+
*/
|
|
59
|
+
public static function uuid($value): ?string
|
|
27
60
|
{
|
|
28
|
-
return
|
|
61
|
+
return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/', $value) ? $value : null;
|
|
29
62
|
}
|
|
30
63
|
|
|
31
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Validate a size string (e.g., "10MB").
|
|
66
|
+
*
|
|
67
|
+
* @param mixed $value The value to validate.
|
|
68
|
+
* @return string|null The valid size string or null if invalid.
|
|
69
|
+
*/
|
|
70
|
+
public static function bytes($value): ?string
|
|
32
71
|
{
|
|
33
|
-
|
|
34
|
-
if ($date && $date->format('Y-m-d') === $value) {
|
|
35
|
-
return $value;
|
|
36
|
-
} else {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
72
|
+
return preg_match('/^[0-9]+[kKmMgGtT]?[bB]?$/', $value) ? $value : null;
|
|
39
73
|
}
|
|
40
74
|
|
|
41
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Validate an XML string.
|
|
77
|
+
*
|
|
78
|
+
* @param mixed $value The value to validate.
|
|
79
|
+
* @return string|null The valid XML string or null if invalid.
|
|
80
|
+
*/
|
|
81
|
+
public static function xml($value): ?string
|
|
42
82
|
{
|
|
43
|
-
|
|
44
|
-
if ($date && $date->format('Y-m-d H:i:s') === $value) {
|
|
45
|
-
return $value;
|
|
46
|
-
} else {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
83
|
+
return preg_match('/^<\?xml/', $value) ? $value : null;
|
|
49
84
|
}
|
|
50
85
|
|
|
51
|
-
|
|
86
|
+
// Number Validation
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Validate an integer value.
|
|
90
|
+
*
|
|
91
|
+
* @param mixed $value The value to validate.
|
|
92
|
+
* @return int|null The integer value or null if invalid.
|
|
93
|
+
*/
|
|
94
|
+
public static function int($value): ?int
|
|
52
95
|
{
|
|
53
|
-
|
|
54
|
-
return json_last_error() === JSON_ERROR_NONE;
|
|
96
|
+
return filter_var($value, FILTER_VALIDATE_INT) !== false ? (int)$value : null;
|
|
55
97
|
}
|
|
56
98
|
|
|
57
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Validate a big integer value.
|
|
101
|
+
*
|
|
102
|
+
* @param mixed $value The value to validate.
|
|
103
|
+
* @return int|null The integer value or null if invalid.
|
|
104
|
+
*/
|
|
105
|
+
public static function bigInt($value): ?int
|
|
58
106
|
{
|
|
59
|
-
return
|
|
107
|
+
return self::int($value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Validate a float value.
|
|
112
|
+
*
|
|
113
|
+
* @param mixed $value The value to validate.
|
|
114
|
+
* @return float|null The float value or null if invalid.
|
|
115
|
+
*/
|
|
116
|
+
public static function float($value): ?float
|
|
117
|
+
{
|
|
118
|
+
return filter_var($value, FILTER_VALIDATE_FLOAT) !== false ? (float)$value : null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Validate a decimal value.
|
|
123
|
+
*
|
|
124
|
+
* @param mixed $value The value to validate.
|
|
125
|
+
* @return float|null The float value or null if invalid.
|
|
126
|
+
*/
|
|
127
|
+
public static function decimal($value): ?float
|
|
128
|
+
{
|
|
129
|
+
return self::float($value);
|
|
60
130
|
}
|
|
61
131
|
|
|
62
|
-
|
|
132
|
+
// Date Validation
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Validate a date in a given format.
|
|
136
|
+
*
|
|
137
|
+
* @param mixed $value The value to validate.
|
|
138
|
+
* @param string $format The date format.
|
|
139
|
+
* @return string|null The valid date string or null if invalid.
|
|
140
|
+
*/
|
|
141
|
+
public static function date($value, string $format = 'Y-m-d'): ?string
|
|
63
142
|
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
143
|
+
$date = \DateTime::createFromFormat($format, $value);
|
|
144
|
+
return $date && $date->format($format) === $value ? $value : null;
|
|
145
|
+
}
|
|
67
146
|
|
|
68
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Validate a datetime in a given format.
|
|
149
|
+
*
|
|
150
|
+
* @param mixed $value The value to validate.
|
|
151
|
+
* @param string $format The datetime format.
|
|
152
|
+
* @return string|null The valid datetime string or null if invalid.
|
|
153
|
+
*/
|
|
154
|
+
public static function dateTime($value, string $format = 'Y-m-d H:i:s'): ?string
|
|
155
|
+
{
|
|
156
|
+
return self::date($value, $format);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Boolean Validation
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Validate a boolean value.
|
|
163
|
+
*
|
|
164
|
+
* @param mixed $value The value to validate.
|
|
165
|
+
* @return bool|null The boolean value or null if invalid.
|
|
166
|
+
*/
|
|
167
|
+
public static function boolean($value): ?bool
|
|
168
|
+
{
|
|
169
|
+
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
|
69
170
|
}
|
|
70
171
|
|
|
71
|
-
|
|
172
|
+
// Other Validation
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Validate a JSON string.
|
|
176
|
+
*
|
|
177
|
+
* @param mixed $value The value to validate.
|
|
178
|
+
* @return bool True if valid JSON, false otherwise.
|
|
179
|
+
*/
|
|
180
|
+
public static function json($value): bool
|
|
72
181
|
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
182
|
+
json_decode($value);
|
|
183
|
+
return json_last_error() === JSON_ERROR_NONE;
|
|
184
|
+
}
|
|
76
185
|
|
|
77
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Validate an enum value against allowed values.
|
|
188
|
+
*
|
|
189
|
+
* @param mixed $value The value to validate.
|
|
190
|
+
* @param array $allowedValues The allowed values.
|
|
191
|
+
* @return bool True if value is allowed, false otherwise.
|
|
192
|
+
*/
|
|
193
|
+
public static function enum($value, array $allowedValues): bool
|
|
194
|
+
{
|
|
195
|
+
return in_array($value, $allowedValues, true);
|
|
78
196
|
}
|
|
79
197
|
}
|
package/dist/src/app/js/index.js
CHANGED
|
@@ -1,342 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Debounces a function to limit the rate at which it is called.
|
|
3
|
-
*
|
|
4
|
-
* The debounced function will postpone its execution until after the specified wait time
|
|
5
|
-
* has elapsed since the last time it was invoked. If `immediate` is `true`, the function
|
|
6
|
-
* will be called at the beginning of the wait period instead of at the end.
|
|
7
|
-
*
|
|
8
|
-
* @param {Function} func - The function to debounce.
|
|
9
|
-
* @param {number} [wait=300] - The number of milliseconds to wait before invoking the function.
|
|
10
|
-
* @param {boolean} [immediate=false] - If `true`, the function is invoked immediately on the leading edge.
|
|
11
|
-
* @returns {Function} - Returns the debounced version of the original function.
|
|
12
|
-
*/
|
|
13
|
-
function debounce(func, wait = 300, immediate = false) {
|
|
14
|
-
let timeout;
|
|
15
|
-
|
|
16
|
-
return function () {
|
|
17
|
-
const context = this;
|
|
18
|
-
const args = arguments;
|
|
19
|
-
clearTimeout(timeout);
|
|
20
|
-
|
|
21
|
-
timeout = setTimeout(() => {
|
|
22
|
-
timeout = null;
|
|
23
|
-
if (!immediate) func.apply(context, args);
|
|
24
|
-
}, wait);
|
|
25
|
-
|
|
26
|
-
if (immediate && !timeout) {
|
|
27
|
-
func.apply(context, args);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Copies text to the clipboard.
|
|
34
|
-
*
|
|
35
|
-
* @param {string} text - The text to copy.
|
|
36
|
-
* @param {HTMLElement} btnElement - The button element that triggered the copy action.
|
|
37
|
-
*/
|
|
38
|
-
function copyToClipboard(text, btnElement) {
|
|
39
|
-
navigator.clipboard.writeText(text).then(
|
|
40
|
-
function () {
|
|
41
|
-
// Clipboard successfully set
|
|
42
|
-
const icon = btnElement.querySelector("i");
|
|
43
|
-
if (icon) {
|
|
44
|
-
icon.className = "fa-regular fa-paste"; // Change to paste icon
|
|
45
|
-
}
|
|
46
|
-
// Set a timeout to change the icon back to copy after 2000 milliseconds
|
|
47
|
-
setTimeout(function () {
|
|
48
|
-
if (icon) {
|
|
49
|
-
icon.className = "fa-regular fa-copy"; // Change back to copy icon
|
|
50
|
-
}
|
|
51
|
-
}, 2000); // 2000 milliseconds delay
|
|
52
|
-
},
|
|
53
|
-
function () {
|
|
54
|
-
// Clipboard write failed
|
|
55
|
-
alert("Failed to copy command to clipboard");
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Copies code to the clipboard.
|
|
62
|
-
*
|
|
63
|
-
* @param {HTMLElement} btnElement - The button element that triggered the copy action.
|
|
64
|
-
*/
|
|
65
|
-
function copyCode(btnElement) {
|
|
66
|
-
// Assuming your code block is uniquely identifiable close to your button
|
|
67
|
-
const codeBlock = btnElement
|
|
68
|
-
.closest(".mockup-code")
|
|
69
|
-
.querySelector("pre code");
|
|
70
|
-
const textToCopy = codeBlock ? codeBlock.textContent : ""; // Get the text content of the code block
|
|
71
|
-
|
|
72
|
-
// Use your existing copyToClipboard function
|
|
73
|
-
copyToClipboard(textToCopy, btnElement);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
typeof RequestApi === "undefined" &&
|
|
78
|
-
typeof StateManager === "undefined"
|
|
79
|
-
) {
|
|
80
|
-
/**
|
|
81
|
-
* Represents a HTTP request.
|
|
82
|
-
*/
|
|
83
|
-
class RequestApi {
|
|
84
|
-
static instance = null;
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* The constructor is now private. To ensure it's not accessible from outside,
|
|
88
|
-
* you can throw an error if someone tries to instantiate it directly
|
|
89
|
-
* (though JavaScript does not have true private constructors).
|
|
90
|
-
*/
|
|
91
|
-
constructor(baseURL = window.location.origin) {
|
|
92
|
-
this.baseURL = baseURL;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Static method to get instance of RequestApi.
|
|
97
|
-
*
|
|
98
|
-
* @param {string} [baseURL=window.location.origin] - The base URL for the request.
|
|
99
|
-
* @returns {RequestApi} The singleton instance of the RequestApi.
|
|
100
|
-
*/
|
|
101
|
-
static getInstance(baseURL = window.location.origin) {
|
|
102
|
-
if (!RequestApi.instance) {
|
|
103
|
-
RequestApi.instance = new RequestApi(baseURL);
|
|
104
|
-
}
|
|
105
|
-
return RequestApi.instance;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Sends a HTTP request.
|
|
110
|
-
*
|
|
111
|
-
* @async
|
|
112
|
-
* @param {string} method - The HTTP method.
|
|
113
|
-
* @param {string} url - The URL to send the request to.
|
|
114
|
-
* @param {*} [data=null] - The data to send with the request.
|
|
115
|
-
* @param {Object} [headers={}] - The headers to include in the request.
|
|
116
|
-
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
117
|
-
*/
|
|
118
|
-
async request(method, url, data = null, headers = {}) {
|
|
119
|
-
let fullUrl = `${this.baseURL}${url}`;
|
|
120
|
-
const options = {
|
|
121
|
-
method,
|
|
122
|
-
headers: {
|
|
123
|
-
"Content-Type": "application/json",
|
|
124
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
125
|
-
...headers,
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
if (data) {
|
|
130
|
-
if (method === "GET") {
|
|
131
|
-
const params = new URLSearchParams(data).toString();
|
|
132
|
-
fullUrl += `?${params}`;
|
|
133
|
-
} else if (method !== "HEAD" && method !== "OPTIONS") {
|
|
134
|
-
options.body = JSON.stringify(data);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
const response = await fetch(fullUrl, options);
|
|
140
|
-
if (method === "HEAD") {
|
|
141
|
-
return response.headers;
|
|
142
|
-
}
|
|
143
|
-
const contentType = response.headers.get("content-type");
|
|
144
|
-
if (contentType && contentType.includes("application/json")) {
|
|
145
|
-
return await response.json();
|
|
146
|
-
} else {
|
|
147
|
-
return await response.text();
|
|
148
|
-
}
|
|
149
|
-
} catch (error) {
|
|
150
|
-
throw error;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Sends a GET request.
|
|
156
|
-
*
|
|
157
|
-
* @param {string} url - The URL to send the request to.
|
|
158
|
-
* @param {*} [params] - The parameters to include in the request.
|
|
159
|
-
* @param {Object} [headers={}] - The headers to include in the request.
|
|
160
|
-
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
161
|
-
*/
|
|
162
|
-
get(url, params, headers) {
|
|
163
|
-
return this.request("GET", url, params, headers);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Sends a POST request.
|
|
168
|
-
*
|
|
169
|
-
* @param {string} url - The URL to send the request to.
|
|
170
|
-
* @param {*} data - The data to send with the request.
|
|
171
|
-
* @param {Object} [headers={}] - The headers to include in the request.
|
|
172
|
-
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
173
|
-
*/
|
|
174
|
-
post(url, data, headers) {
|
|
175
|
-
return this.request("POST", url, data, headers);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Sends a PUT request.
|
|
180
|
-
*
|
|
181
|
-
* @param {string} url - The URL to send the request to.
|
|
182
|
-
* @param {*} data - The data to send with the request.
|
|
183
|
-
* @param {Object} [headers={}] - The headers to include in the request.
|
|
184
|
-
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
185
|
-
*/
|
|
186
|
-
put(url, data, headers) {
|
|
187
|
-
return this.request("PUT", url, data, headers);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Sends a DELETE request.
|
|
192
|
-
*
|
|
193
|
-
* @param {string} url - The URL to send the request to.
|
|
194
|
-
* @param {*} data - The data to send with the request.
|
|
195
|
-
* @param {Object} [headers={}] - The headers to include in the request.
|
|
196
|
-
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
197
|
-
*/
|
|
198
|
-
delete(url, data, headers) {
|
|
199
|
-
return this.request("DELETE", url, data, headers);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Sends a PATCH request.
|
|
204
|
-
*
|
|
205
|
-
* @param {string} url - The URL to send the request to.
|
|
206
|
-
* @param {*} data - The data to send with the request.
|
|
207
|
-
* @param {Object} [headers={}] - The headers to include in the request.
|
|
208
|
-
* @returns {Promise<unknown>} - A promise that resolves to the response data.
|
|
209
|
-
*/
|
|
210
|
-
patch(url, data, headers) {
|
|
211
|
-
return this.request("PATCH", url, data, headers);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Sends a HEAD request.
|
|
216
|
-
*
|
|
217
|
-
* @param {string} url - The URL to send the request to.
|
|
218
|
-
* @param {Object} [headers={}] - The headers to include in the request.
|
|
219
|
-
* @returns {Promise<unknown>} - A promise that resolves to the response headers.
|
|
220
|
-
*/
|
|
221
|
-
head(url, headers) {
|
|
222
|
-
return this.request("HEAD", url, null, headers);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Sends an OPTIONS request.
|
|
227
|
-
*
|
|
228
|
-
* @param {string} url - The URL to send the request to.
|
|
229
|
-
* @param {Object} [headers={}] - The headers to include in the request.
|
|
230
|
-
* @returns {Promise<unknown>} - A promise that resolves to the options available.
|
|
231
|
-
*/
|
|
232
|
-
options(url, headers) {
|
|
233
|
-
return this.request("OPTIONS", url, null, headers);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Manages the application state.
|
|
239
|
-
*/
|
|
240
|
-
class StateManager {
|
|
241
|
-
static instance = null;
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Creates a new StateManager instance.
|
|
245
|
-
*
|
|
246
|
-
* @param {{}} [initialState={}] - The initial state.
|
|
247
|
-
*/
|
|
248
|
-
constructor(initialState = {}) {
|
|
249
|
-
this.state = initialState;
|
|
250
|
-
this.listeners = [];
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Gets the singleton instance of StateManager.
|
|
255
|
-
*
|
|
256
|
-
* @static
|
|
257
|
-
* @param {{}} [initialState={}] - The initial state.
|
|
258
|
-
* @returns {StateManager} - The StateManager instance.
|
|
259
|
-
*/
|
|
260
|
-
static getInstance(initialState = {}) {
|
|
261
|
-
if (!StateManager.instance) {
|
|
262
|
-
StateManager.instance = new StateManager(initialState);
|
|
263
|
-
StateManager.instance.loadState(); // Load state immediately after instance creation
|
|
264
|
-
}
|
|
265
|
-
return StateManager.instance;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Sets the state.
|
|
270
|
-
*
|
|
271
|
-
* @param {*} update - The state update.
|
|
272
|
-
* @param {boolean} [saveToStorage=false] - Whether to save the state to localStorage.
|
|
273
|
-
*/
|
|
274
|
-
setState(update, saveToStorage = false) {
|
|
275
|
-
this.state = { ...this.state, ...update };
|
|
276
|
-
this.listeners.forEach((listener) => listener(this.state));
|
|
277
|
-
if (saveToStorage) {
|
|
278
|
-
this.saveState();
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Subscribes to state changes.
|
|
284
|
-
*
|
|
285
|
-
* @param {*} listener - The listener function.
|
|
286
|
-
* @returns {Function} - A function to unsubscribe the listener.
|
|
287
|
-
*/
|
|
288
|
-
subscribe(listener) {
|
|
289
|
-
this.listeners.push(listener);
|
|
290
|
-
listener(this.state); // Immediately invoke the listener with the current state
|
|
291
|
-
return () =>
|
|
292
|
-
(this.listeners = this.listeners.filter((l) => l !== listener));
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Saves the state to localStorage.
|
|
297
|
-
*/
|
|
298
|
-
saveState() {
|
|
299
|
-
localStorage.setItem("appState", JSON.stringify(this.state));
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Loads the state from localStorage.
|
|
304
|
-
*/
|
|
305
|
-
loadState() {
|
|
306
|
-
const state = localStorage.getItem("appState");
|
|
307
|
-
if (state) {
|
|
308
|
-
this.state = JSON.parse(state);
|
|
309
|
-
this.listeners.forEach((listener) => listener(this.state));
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Resets the state to its initial value.
|
|
315
|
-
*
|
|
316
|
-
* @param {boolean} [clearFromStorage=false] - Whether to clear the state from localStorage.
|
|
317
|
-
*/
|
|
318
|
-
resetState(clearFromStorage = false) {
|
|
319
|
-
this.state = {}; // Reset the state to an empty object or a default state if you prefer
|
|
320
|
-
this.listeners.forEach((listener) => listener(this.state));
|
|
321
|
-
if (clearFromStorage) {
|
|
322
|
-
localStorage.removeItem("appState"); // Clear the state from localStorage
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
let store = null;
|
|
328
|
-
let api = null;
|
|
329
|
-
|
|
330
|
-
// Function to initialize instances
|
|
331
|
-
function initializeInstances() {
|
|
332
|
-
store = StateManager.getInstance();
|
|
333
|
-
api = RequestApi.getInstance();
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Initialize instances on initial page load
|
|
337
|
-
document.addEventListener("DOMContentLoaded", function () {
|
|
338
|
-
initializeInstances();
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
"use strict";var eventAttributes=["onclick","ondblclick","onmousedown","onmouseup","onmouseover","onmousemove","onmouseout","onwheel","onkeypress","onkeydown","onkeyup","onfocus","onblur","onchange","oninput","onselect","onsubmit","onreset","onresize","onscroll","onload","onunload","onabort","onerror","onbeforeunload","oncopy","oncut","onpaste","ondrag","ondragstart","ondragend","ondragover","ondragenter","ondragleave","ondrop","oncontextmenu","ontouchstart","ontouchmove","ontouchend","ontouchcancel","onpointerdown","onpointerup","onpointermove","onpointerover","onpointerout","onpointerenter","onpointerleave","onpointercancel"];function attachWireFunctionEvents(){document.querySelectorAll("button, input, select, textarea, a, form, label, div, span").forEach((e=>{e instanceof HTMLAnchorElement&&e.addEventListener("click",handleAnchorTag),eventAttributes.forEach((t=>{const n=e.getAttribute(t),o=t.slice(2);n&&(e.removeAttribute(t),e.addEventListener(o,(t=>{t.preventDefault();const{funcName:o,data:r}=parseCallback(e,n);if(o){const e=window[o];if("function"==typeof e)try{e(...r)}catch(e){}else handleUndefinedFunction(o,r)}})))}))}))}async function handleAnchorTag(e){const t=e.currentTarget,n=t.getAttribute("href"),o=t.getAttribute("target");if(!n||"_blank"===o||e.metaKey||e.ctrlKey)return;e.preventDefault();if(/^(https?:)?\/\//i.test(n)&&!n.startsWith(window.location.origin))window.location.href=n;else try{history.pushState(null,"",n),window.dispatchEvent(new PopStateEvent("popstate",{state:null}))}catch(e){}}function updateDocumentContent(e){if(e.includes("<!DOCTYPE html>")){const t=(new DOMParser).parseFromString(e,"text/html");document.replaceChild(document.adoptNode(t.documentElement),document.documentElement),attachWireFunctionEvents()}else{const t=document.activeElement,n=t?.id||t?.name,o=t?.value,r=t?.selectionStart,a=t?.selectionEnd,c=(new DOMParser).parseFromString(e,"text/html");if(updateElementContent(document.body,c.body),n){const e=document.getElementById(n)||document.querySelector(`[name="${n}"]`);e instanceof HTMLInputElement&&(e.focus(),e.value=o||"",null!==r&&null!==a&&e.setSelectionRange(r,a))}attachWireFunctionEvents()}}function updateElementContent(e,t){e.innerHTML!==t.innerHTML&&(e.innerHTML=t.innerHTML),Array.from(t.attributes).forEach((t=>{e.setAttribute(t.name,t.value)}))}function parseCallback(e,t){let n={};if(e instanceof HTMLFormElement){const t=new FormData(e);n=Object.fromEntries(t.entries())}else e instanceof HTMLInputElement&&(e.name?"checkbox"===e.type||"radio"===e.type?n[e.name]=e.checked:n[e.name]=e.value:"checkbox"===e.type||"radio"===e.type?n.value=e.checked:n.value=e.value);const o=t.match(/(\w+)\(([^)]*)\)/);if(o){const e=o[1];let t=[];return o[2]&&(t=o[2].split(/,(?=(?:[^'"]*['"][^'"]*['"])*[^'"]*$)/).map((e=>e.trim())),t=t.map((e=>{try{const t=e.replace(/'/g,'"');return JSON.parse(t)}catch{return e}}))),t.forEach((e=>{n="object"==typeof e&&null!==e?{...n,...e}:{...n,args:t}})),{funcName:e,data:n}}return{funcName:t,data:n}}function handleUndefinedFunction(e,t){const n={callback:e,...t},o={method:"POST",headers:{"Content-Type":"application/json","X-Requested-With":"XMLHttpRequest",HTTP_PPHP_WIRE_REQUEST:"true"},body:JSON.stringify(n)},r={method:"POST",headers:{"Content-Type":"application/json","X-Requested-With":"XMLHttpRequest",HTTP_PPHP_WIRE_REQUEST:"true"}},a=async e=>{const t=await fetch(window.location.pathname,e);return await t.text()};let c="";a(o).then((e=>(""===c&&(c=e),a(r)))).then((e=>{updateDocumentContent(mergeAndReplaceDuplicates(c,e))})).catch((e=>{}))}function mergeAndReplaceDuplicates(e,t){const{html:n,scripts:o}=extractScripts(e),{html:r,scripts:a}=extractScripts(t),c=(new DOMParser).parseFromString(r,"text/html"),i=document.createElement("div");i.innerHTML=n,mergeElementsCompletely(i,c.body);const s=i.innerHTML;return mergeScripts(o,a)+s}function mergeElementsCompletely(e,t){Array.from(e.attributes).forEach((t=>e.removeAttribute(t.name))),Array.from(t.attributes).forEach((t=>e.setAttribute(t.name,t.value))),t.textContent&&!t.children.length&&(e.textContent=t.textContent);const n=Array.from(e.children),o=Array.from(t.children);n.forEach((t=>e.removeChild(t))),o.forEach((t=>e.appendChild(t.cloneNode(!0))))}function extractScripts(e){let t="",n=e;return n=n.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,(e=>(t+=e,""))),{html:n,scripts:t}}function mergeScripts(e,t){const n=new Set([...e.match(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi)||[],...t.match(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi)||[]]);return Array.from(n).join("\n")}document.addEventListener("DOMContentLoaded",(()=>{attachWireFunctionEvents()})),window.addEventListener("popstate",(async()=>{try{const e=await fetch(window.location.href,{headers:{"X-Requested-With":"XMLHttpRequest"}});updateDocumentContent(await e.text())}catch(e){}}));
|
|
1
|
+
"use strict";var eventAttributes=["onclick","ondblclick","onmousedown","onmouseup","onmouseover","onmousemove","onmouseout","onwheel","onkeypress","onkeydown","onkeyup","onfocus","onblur","onchange","oninput","onselect","onsubmit","onreset","onresize","onscroll","onload","onunload","onabort","onerror","onbeforeunload","oncopy","oncut","onpaste","ondrag","ondragstart","ondragend","ondragover","ondragenter","ondragleave","ondrop","oncontextmenu","ontouchstart","ontouchmove","ontouchend","ontouchcancel","onpointerdown","onpointerup","onpointermove","onpointerover","onpointerout","onpointerenter","onpointerleave","onpointercancel"];function attachWireFunctionEvents(){document.querySelectorAll("button, input, select, textarea, a, form, label, div, span").forEach((t=>{t instanceof HTMLAnchorElement&&t.addEventListener("click",handleAnchorTag),eventAttributes.forEach((e=>{const n=t.getAttribute(e),o=e.slice(2);n&&(t.removeAttribute(e),handleDebounce(t,o,n))}))}))}function handleDebounce(t,e,n){const o=t.getAttribute("pp-debounce");if(o){const a=debounce((()=>{invokeHandler(t,n)}),parseInt(o,10));t.addEventListener(e,a)}else t.addEventListener(e,(e=>{e.preventDefault(),invokeHandler(t,n)}))}function invokeHandler(t,e){const{funcName:n,data:o}=parseCallback(t,e);if(n){const t=window[n];if("function"==typeof t)try{t(...o)}catch(t){}else handleUndefinedFunction(n,o)}}async function handleAnchorTag(t){const e=t.currentTarget,n=e.getAttribute("href"),o=e.getAttribute("target");if(!n||"_blank"===o||t.metaKey||t.ctrlKey)return;t.preventDefault();if(/^(https?:)?\/\//i.test(n)&&!n.startsWith(window.location.origin))window.location.href=n;else try{history.pushState(null,"",n),window.dispatchEvent(new PopStateEvent("popstate",{state:null}))}catch(t){}}document.addEventListener("DOMContentLoaded",attachWireFunctionEvents);const state={checkedElements:new Set};function updateDocumentContent(t){if(t.includes("<!DOCTYPE html>")){const e=(new DOMParser).parseFromString(t,"text/html");document.replaceChild(document.adoptNode(e.documentElement),document.documentElement),attachWireFunctionEvents()}else{saveState();const e=saveScrollPositions(),n=(new DOMParser).parseFromString(t,"text/html");updateElementContent(document.body,n.body),restoreState(),restoreScrollPositions(e),attachWireFunctionEvents()}}function saveState(){const t=document.activeElement;state.focusId=t?.id||t?.name,state.focusValue=t?.value,state.focusSelectionStart=t?.selectionStart,state.focusSelectionEnd=t?.selectionEnd,state.checkedElements.clear(),document.querySelectorAll('input[type="checkbox"]:checked').forEach((t=>{state.checkedElements.add(t.id||t.name)}))}function restoreState(){if(state.focusId){const t=document.getElementById(state.focusId)||document.querySelector(`[name="${state.focusId}"]`);t instanceof HTMLInputElement&&(t.focus(),"search"===t.type&&(t.value=state.focusValue||""),void 0!==state.focusSelectionStart&&null!==state.focusSelectionEnd&&t.setSelectionRange(state.focusSelectionStart||null,state.focusSelectionEnd||null))}state.checkedElements.forEach((t=>{const e=document.getElementById(t);e&&(e.checked=!0)}))}function saveScrollPositions(){const t={};return document.querySelectorAll("*").forEach((e=>{(e.scrollTop||e.scrollLeft)&&(t[getElementKey(e)]={scrollTop:e.scrollTop,scrollLeft:e.scrollLeft})})),t}function restoreScrollPositions(t){document.querySelectorAll("*").forEach((e=>{const n=getElementKey(e);t[n]&&(e.scrollTop=t[n].scrollTop,e.scrollLeft=t[n].scrollLeft)}))}function getElementKey(t){return t.id||t.className||t.tagName}function updateElementContent(t,e){t.innerHTML!==e.innerHTML&&(t.innerHTML=e.innerHTML),Array.from(e.attributes).forEach((e=>{t.getAttribute(e.name)!==e.value&&t.setAttribute(e.name,e.value)})),Array.from(t.attributes).forEach((n=>{e.hasAttribute(n.name)||t.removeAttribute(n.name)}))}function parseCallback(t,e){let n={};if(t instanceof HTMLFormElement){const e=new FormData(t);n=Object.fromEntries(e.entries())}else t instanceof HTMLInputElement&&(t.name?"checkbox"===t.type||"radio"===t.type?n[t.name]=t.checked:n[t.name]=t.value:"checkbox"===t.type||"radio"===t.type?n.value=t.checked:n.value=t.value);const o=e.match(/(\w+)\(([^)]*)\)/);if(o){const t=o[1];let e=[];return o[2]&&(e=o[2].split(/,(?=(?:[^'"]*['"][^'"]*['"])*[^'"]*$)/).map((t=>t.trim())),e=e.map((t=>{try{const e=t.replace(/'/g,'"');return JSON.parse(e)}catch{return t}}))),e.forEach((t=>{n="object"==typeof t&&null!==t?{...n,...t}:{...n,args:e}})),{funcName:t,data:n}}return{funcName:e,data:n}}async function handleUndefinedFunction(t,e){const n={callback:t,...e},o={method:"POST",headers:{"Content-Type":"application/json","X-Requested-With":"XMLHttpRequest",HTTP_PPHP_WIRE_REQUEST:"true"},body:JSON.stringify(n)},a={method:"POST",headers:{"Content-Type":"application/json","X-Requested-With":"XMLHttpRequest",HTTP_PPHP_WIRE_REQUEST:"true"},body:JSON.stringify({secondRequest:!0})},s=async t=>{const e=await fetch(window.location.pathname,t);return await e.text()};try{const t=await s(o),e=await s(a);updateDocumentContent(t+e)}catch(t){}}function debounce(t,e=300,n=!1){let o;return function(...a){const s=this;o&&clearTimeout(o),o=setTimeout((()=>{o=null,n||t.apply(s,a)}),e),n&&!o&&t.apply(s,a)}}function copyCode(t,e,n,o,a=2e3){const s=t.closest(`.${e}`)?.querySelector("pre code"),c=s?.textContent?.trim()||"";c?navigator.clipboard.writeText(c).then((()=>{const e=t.querySelector("i");e&&(e.className=o),setTimeout((()=>{e&&(e.className=n)}),a)}),(()=>{alert("Failed to copy command to clipboard")})):alert("Failed to find the code block to copy")}window.addEventListener("popstate",(async()=>{try{const t=await fetch(window.location.href,{headers:{"X-Requested-With":"XMLHttpRequest"}});updateDocumentContent(await t.text())}catch(t){}}));let api=null;if(void 0===api){class t{static instance=null;baseURL;constructor(t=window.location.origin){this.baseURL=t}static getInstance(e=window.location.origin){return t.instance||(t.instance=new t(e)),t.instance}async request(t,e,n=null,o={}){let a=`${this.baseURL}${e}`;const s={method:t,headers:{"Content-Type":"application/json","X-Requested-With":"XMLHttpRequest",...o}};if(n)if("GET"===t){a+=`?${new URLSearchParams(n).toString()}`}else"HEAD"!==t&&"OPTIONS"!==t&&(s.body=JSON.stringify(n));try{const e=await fetch(a,s);if("HEAD"===t)return e.headers;const n=e.headers.get("content-type");return n&&n.includes("application/json")?await e.json():await e.text()}catch(t){throw t}}get(t,e,n){return this.request("GET",t,e,n)}post(t,e,n){return this.request("POST",t,e,n)}put(t,e,n){return this.request("PUT",t,e,n)}delete(t,e,n){return this.request("DELETE",t,e,n)}patch(t,e,n){return this.request("PATCH",t,e,n)}head(t,e){return this.request("HEAD",t,null,e)}options(t,e){return this.request("OPTIONS",t,null,e)}}api=t.getInstance()}let store=null;if(void 0===store){class t{static instance=null;state;listeners;constructor(t={}){this.state=t,this.listeners=[]}static getInstance(e={}){return t.instance||(t.instance=new t(e),t.instance.loadState()),t.instance}setState(t,e=!1){this.state={...this.state,...t},this.listeners.forEach((t=>t(this.state))),e&&this.saveState()}subscribe(t){return this.listeners.push(t),t(this.state),()=>{this.listeners=this.listeners.filter((e=>e!==t))}}saveState(){localStorage.setItem("appState",JSON.stringify(this.state))}loadState(){const t=localStorage.getItem("appState");t&&(this.state=JSON.parse(t),this.listeners.forEach((t=>t(this.state))))}resetState(t=!1){this.state={},this.listeners.forEach((t=>t(this.state))),t&&localStorage.removeItem("appState")}}store=t.getInstance()}
|