create-prisma-php-app 1.15.34 → 1.15.36
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/src/app/js/index.js +1 -1097
- package/package.json +1 -1
package/dist/src/app/js/index.js
CHANGED
|
@@ -1,1097 +1 @@
|
|
|
1
|
-
var eventAttributes = [
|
|
2
|
-
"onclick",
|
|
3
|
-
"ondblclick",
|
|
4
|
-
"onmousedown",
|
|
5
|
-
"onmouseup",
|
|
6
|
-
"onmouseover",
|
|
7
|
-
"onmousemove",
|
|
8
|
-
"onmouseout",
|
|
9
|
-
"onwheel",
|
|
10
|
-
"onkeypress",
|
|
11
|
-
"onkeydown",
|
|
12
|
-
"onkeyup",
|
|
13
|
-
"onfocus",
|
|
14
|
-
"onblur",
|
|
15
|
-
"onchange",
|
|
16
|
-
"oninput",
|
|
17
|
-
"onselect",
|
|
18
|
-
"onsubmit",
|
|
19
|
-
"onreset",
|
|
20
|
-
"onresize",
|
|
21
|
-
"onscroll",
|
|
22
|
-
"onload",
|
|
23
|
-
"onunload",
|
|
24
|
-
"onabort",
|
|
25
|
-
"onerror",
|
|
26
|
-
"onbeforeunload",
|
|
27
|
-
"oncopy",
|
|
28
|
-
"oncut",
|
|
29
|
-
"onpaste",
|
|
30
|
-
"ondrag",
|
|
31
|
-
"ondragstart",
|
|
32
|
-
"ondragend",
|
|
33
|
-
"ondragover",
|
|
34
|
-
"ondragenter",
|
|
35
|
-
"ondragleave",
|
|
36
|
-
"ondrop",
|
|
37
|
-
"oncontextmenu",
|
|
38
|
-
"ontouchstart",
|
|
39
|
-
"ontouchmove",
|
|
40
|
-
"ontouchend",
|
|
41
|
-
"ontouchcancel",
|
|
42
|
-
"onpointerdown",
|
|
43
|
-
"onpointerup",
|
|
44
|
-
"onpointermove",
|
|
45
|
-
"onpointerover",
|
|
46
|
-
"onpointerout",
|
|
47
|
-
"onpointerenter",
|
|
48
|
-
"onpointerleave",
|
|
49
|
-
"onpointercancel",
|
|
50
|
-
];
|
|
51
|
-
document.addEventListener("DOMContentLoaded", attachWireFunctionEvents);
|
|
52
|
-
var state = {
|
|
53
|
-
checkedElements: new Set(),
|
|
54
|
-
};
|
|
55
|
-
var responseData = null;
|
|
56
|
-
var store = null;
|
|
57
|
-
var isNavigating = false;
|
|
58
|
-
function attachWireFunctionEvents() {
|
|
59
|
-
handleHiddenAttribute();
|
|
60
|
-
const interactiveElements = document.querySelectorAll(
|
|
61
|
-
"button, input, select, textarea, a, form, label, div, span"
|
|
62
|
-
);
|
|
63
|
-
interactiveElements.forEach((element) => {
|
|
64
|
-
handleAnchorTag(element);
|
|
65
|
-
eventAttributes.forEach((attr) => {
|
|
66
|
-
const originalHandler = element.getAttribute(attr);
|
|
67
|
-
const eventType = attr.slice(2); // Get the event type (e.g., "click" from "onclick")
|
|
68
|
-
if (originalHandler) {
|
|
69
|
-
element.removeAttribute(attr);
|
|
70
|
-
handleDebounce(element, eventType, originalHandler);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
// Special handling for form submit
|
|
74
|
-
if (element instanceof HTMLFormElement) {
|
|
75
|
-
const submitHandler = element.getAttribute("onsubmit");
|
|
76
|
-
if (submitHandler) {
|
|
77
|
-
element.removeAttribute("onsubmit");
|
|
78
|
-
handleDebounce(element, "submit", submitHandler);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
function handleHiddenAttribute() {
|
|
84
|
-
const hiddenElements = document.querySelectorAll("[pp-hidden]");
|
|
85
|
-
hiddenElements.forEach((element) => {
|
|
86
|
-
let hiddenAttr = element.getAttribute("pp-hidden");
|
|
87
|
-
if (!hiddenAttr) return;
|
|
88
|
-
// Determine if the attribute is JSON-like or a simple time value
|
|
89
|
-
if (isJsonLike(hiddenAttr)) {
|
|
90
|
-
try {
|
|
91
|
-
// Normalize single quotes to double quotes for JSON parsing
|
|
92
|
-
const config = parseJson(hiddenAttr);
|
|
93
|
-
handleElementVisibility(element, config);
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error("JSON parsing error:", error);
|
|
96
|
-
}
|
|
97
|
-
} else {
|
|
98
|
-
// Handle non-JSON attribute as simple time value
|
|
99
|
-
const timeout = parseTime(hiddenAttr);
|
|
100
|
-
if (timeout > 0) {
|
|
101
|
-
// Directly schedule hiding after the timeout for non-JSON value
|
|
102
|
-
scheduleVisibilityChange(element, timeout, "hidden");
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
// Function to determine if a string is JSON-like
|
|
108
|
-
function isJsonLike(str) {
|
|
109
|
-
// A simple check to see if the string starts with '{' and ends with '}'
|
|
110
|
-
return str.trim().startsWith("{") && str.trim().endsWith("}");
|
|
111
|
-
}
|
|
112
|
-
// Function to handle visibility changes based on config
|
|
113
|
-
function handleElementVisibility(element, config) {
|
|
114
|
-
const startTimeout = config.start ? parseTime(config.start) : 0;
|
|
115
|
-
const endTimeout = config.end ? parseTime(config.end) : 0;
|
|
116
|
-
if (startTimeout > 0) {
|
|
117
|
-
// Start hidden and show after startTimeout
|
|
118
|
-
element.style.visibility = "hidden";
|
|
119
|
-
scheduleVisibilityChange(element, startTimeout, "visible");
|
|
120
|
-
// If endTimeout is also specified, hide again after endTimeout relative to startTimeout
|
|
121
|
-
if (endTimeout > 0) {
|
|
122
|
-
scheduleVisibilityChange(element, startTimeout + endTimeout, "hidden");
|
|
123
|
-
}
|
|
124
|
-
} else if (endTimeout > 0) {
|
|
125
|
-
// If no startTimeout but endTimeout is specified, only hide after endTimeout
|
|
126
|
-
scheduleVisibilityChange(element, endTimeout, "hidden");
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
// Function to schedule visibility changes using requestAnimationFrame
|
|
130
|
-
function scheduleVisibilityChange(element, timeout, visibility) {
|
|
131
|
-
setTimeout(() => {
|
|
132
|
-
requestAnimationFrame(() => {
|
|
133
|
-
element.style.visibility = visibility;
|
|
134
|
-
});
|
|
135
|
-
}, timeout);
|
|
136
|
-
}
|
|
137
|
-
// Function to parse time strings into milliseconds
|
|
138
|
-
function parseTime(time) {
|
|
139
|
-
if (typeof time === "number") {
|
|
140
|
-
return time;
|
|
141
|
-
}
|
|
142
|
-
const match = time.match(/^(\d+)(ms|s|m)?$/);
|
|
143
|
-
if (match) {
|
|
144
|
-
const value = parseInt(match[1], 10);
|
|
145
|
-
const unit = match[2] || "ms"; // Default to milliseconds if no unit specified
|
|
146
|
-
switch (unit) {
|
|
147
|
-
case "ms":
|
|
148
|
-
return value;
|
|
149
|
-
case "s":
|
|
150
|
-
return value * 1000;
|
|
151
|
-
case "m":
|
|
152
|
-
return value * 60 * 1000;
|
|
153
|
-
default:
|
|
154
|
-
return value; // Default to milliseconds
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return 0; // Default to 0 if parsing fails
|
|
158
|
-
}
|
|
159
|
-
async function handleDebounce(element, eventType, originalHandler) {
|
|
160
|
-
const debounceTime = element.getAttribute("pp-debounce") || "";
|
|
161
|
-
const executeFirst = element.getAttribute("pp-trigger") || "";
|
|
162
|
-
const targetOnlyAttribute = element.getAttribute("pp-target-only") || "";
|
|
163
|
-
const combinedHandler = async (event) => {
|
|
164
|
-
event.preventDefault();
|
|
165
|
-
try {
|
|
166
|
-
if (executeFirst || targetOnlyAttribute) {
|
|
167
|
-
if (targetOnlyAttribute) {
|
|
168
|
-
invokeHandler(element, executeFirst);
|
|
169
|
-
} else {
|
|
170
|
-
await invokeHandler(element, executeFirst);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
// Execute the original handler
|
|
174
|
-
await invokeHandler(element, originalHandler);
|
|
175
|
-
} catch (error) {
|
|
176
|
-
console.error("Error in debounced handler:", error);
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
if (debounceTime) {
|
|
180
|
-
const wait = parseTime(debounceTime);
|
|
181
|
-
const debouncedHandler = debounce(combinedHandler, wait);
|
|
182
|
-
if (element instanceof HTMLFormElement && eventType === "submit") {
|
|
183
|
-
element.addEventListener(eventType, (event) => {
|
|
184
|
-
event.preventDefault();
|
|
185
|
-
debouncedHandler(event);
|
|
186
|
-
});
|
|
187
|
-
} else {
|
|
188
|
-
element.addEventListener(eventType, debouncedHandler);
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
element.addEventListener(eventType, combinedHandler);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
async function invokeHandler(element, handler) {
|
|
195
|
-
try {
|
|
196
|
-
// Extract method details from the handler string
|
|
197
|
-
const methodMatch = handler.match(/^(\w+(\.\w+)*)\((.*)\)$/);
|
|
198
|
-
if (methodMatch) {
|
|
199
|
-
const fullMethodName = methodMatch[1];
|
|
200
|
-
const argsString = methodMatch[3];
|
|
201
|
-
// Resolve context for nested methods
|
|
202
|
-
const methodParts = fullMethodName.split(".");
|
|
203
|
-
const { context, methodName } = resolveContext(methodParts);
|
|
204
|
-
// Check if the resolved method is a function
|
|
205
|
-
if (typeof context[methodName] === "function") {
|
|
206
|
-
const args = parseArguments(argsString);
|
|
207
|
-
await context[methodName](...args);
|
|
208
|
-
} else {
|
|
209
|
-
await handleParsedCallback(element, handler);
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
212
|
-
await handleParsedCallback(element, handler);
|
|
213
|
-
}
|
|
214
|
-
} catch (error) {
|
|
215
|
-
console.error(`Error executing handler ${handler}:`, error);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Resolve the context and method name for nested methods.
|
|
220
|
-
* @param methodParts - Array of method parts split by dots.
|
|
221
|
-
* @returns Object containing the resolved context and method name.
|
|
222
|
-
*/
|
|
223
|
-
function resolveContext(methodParts) {
|
|
224
|
-
let context = window;
|
|
225
|
-
for (let i = 0; i < methodParts.length - 1; i++) {
|
|
226
|
-
context = context[methodParts[i]];
|
|
227
|
-
if (!context) {
|
|
228
|
-
throw new Error(`Cannot find object ${methodParts[i]} in the context.`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return { context, methodName: methodParts[methodParts.length - 1] };
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Parse arguments string into an array.
|
|
235
|
-
* @param argsString - Comma-separated string of arguments.
|
|
236
|
-
* @returns Parsed arguments as an array.
|
|
237
|
-
*/
|
|
238
|
-
function parseArguments(argsString) {
|
|
239
|
-
return argsString ? JSON.parse(`[${argsString}]`) : [];
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Handle parsing and executing the callback from the element's handler.
|
|
243
|
-
* @param element - HTML element that initiated the handler.
|
|
244
|
-
* @param handler - Handler string to be parsed and executed.
|
|
245
|
-
*/
|
|
246
|
-
async function handleParsedCallback(element, handler) {
|
|
247
|
-
const { funcName, data } = parseCallback(element, handler);
|
|
248
|
-
if (!funcName) return;
|
|
249
|
-
const func = window[funcName];
|
|
250
|
-
if (typeof func === "function") {
|
|
251
|
-
const args = Array.isArray(data.args) ? data.args : [];
|
|
252
|
-
const responseJSON = responseData ? parseJson(responseData) : null;
|
|
253
|
-
await func(...args, element, data, responseJSON);
|
|
254
|
-
} else {
|
|
255
|
-
responseData = await handleUndefinedFunction(element, funcName, data);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
function handleAnchorTag(element) {
|
|
259
|
-
if (!(element instanceof HTMLAnchorElement)) return;
|
|
260
|
-
element.addEventListener("click", async (event) => {
|
|
261
|
-
const anchor = event.currentTarget;
|
|
262
|
-
const href = anchor.getAttribute("href");
|
|
263
|
-
const target = anchor.getAttribute("target");
|
|
264
|
-
// If there's no href, or the target is _blank, or the meta key is pressed, allow default behavior
|
|
265
|
-
if (!href || target === "_blank" || event.metaKey || event.ctrlKey) {
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
event.preventDefault(); // Prevent the default navigation
|
|
269
|
-
if (isNavigating) return; // Prevent multiple navigations
|
|
270
|
-
isNavigating = true;
|
|
271
|
-
try {
|
|
272
|
-
const isExternal =
|
|
273
|
-
/^(https?:)?\/\//i.test(href) &&
|
|
274
|
-
!href.startsWith(window.location.origin);
|
|
275
|
-
if (isExternal) {
|
|
276
|
-
// If the link is external, use the default behavior
|
|
277
|
-
window.location.href = href;
|
|
278
|
-
} else {
|
|
279
|
-
// If the link is internal, use history.pushState
|
|
280
|
-
history.pushState(null, "", href);
|
|
281
|
-
await handleNavigation();
|
|
282
|
-
}
|
|
283
|
-
} catch (error) {
|
|
284
|
-
console.error("Anchor click error:", error);
|
|
285
|
-
} finally {
|
|
286
|
-
isNavigating = false;
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
// Handle browser's back/forward navigation
|
|
291
|
-
window.addEventListener("popstate", async () => {
|
|
292
|
-
await handleNavigation();
|
|
293
|
-
});
|
|
294
|
-
async function handleNavigation() {
|
|
295
|
-
try {
|
|
296
|
-
const data = await pphpFetch(window.location.href);
|
|
297
|
-
updateDocumentContent(data);
|
|
298
|
-
} catch (error) {
|
|
299
|
-
console.error("Navigation error:", error);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
function updateDocumentContent(data) {
|
|
303
|
-
if (data.includes("<!DOCTYPE html>")) {
|
|
304
|
-
const newDoc = new DOMParser().parseFromString(data, "text/html");
|
|
305
|
-
// Function to extract and clone scripts
|
|
306
|
-
const extractAndCloneScripts = (container) => {
|
|
307
|
-
const scriptElements = container.querySelectorAll("script");
|
|
308
|
-
const scripts = [];
|
|
309
|
-
scriptElements.forEach((script) => {
|
|
310
|
-
const newScript = document.createElement("script");
|
|
311
|
-
newScript.type = script.type || "text/javascript";
|
|
312
|
-
if (script.src) {
|
|
313
|
-
newScript.src = script.src;
|
|
314
|
-
} else {
|
|
315
|
-
newScript.textContent = script.textContent;
|
|
316
|
-
}
|
|
317
|
-
scripts.push(newScript);
|
|
318
|
-
script.remove();
|
|
319
|
-
});
|
|
320
|
-
return scripts;
|
|
321
|
-
};
|
|
322
|
-
// Extract scripts from head and body of the new document
|
|
323
|
-
const headScripts = extractAndCloneScripts(newDoc.head);
|
|
324
|
-
const bodyScripts = extractAndCloneScripts(newDoc.body);
|
|
325
|
-
// Replace the document content
|
|
326
|
-
document.replaceChild(
|
|
327
|
-
document.adoptNode(newDoc.documentElement),
|
|
328
|
-
document.documentElement
|
|
329
|
-
);
|
|
330
|
-
// Append and execute the extracted head scripts
|
|
331
|
-
headScripts.forEach((script) => document.head.appendChild(script));
|
|
332
|
-
// Append and execute the extracted body scripts
|
|
333
|
-
bodyScripts.forEach((script) => document.body.appendChild(script));
|
|
334
|
-
} else {
|
|
335
|
-
saveState();
|
|
336
|
-
const scrollPositions = saveScrollPositions();
|
|
337
|
-
const parser = new DOMParser();
|
|
338
|
-
const newDoc = parser.parseFromString(data, "text/html");
|
|
339
|
-
const scripts = Array.from(newDoc.body.querySelectorAll("script"));
|
|
340
|
-
const styles = Array.from(newDoc.head.querySelectorAll("style"));
|
|
341
|
-
const newBody = newDoc.body;
|
|
342
|
-
diffAndPatch(document.body, newBody);
|
|
343
|
-
restoreState();
|
|
344
|
-
restoreScrollPositions(scrollPositions);
|
|
345
|
-
// Load styles
|
|
346
|
-
styles.forEach((style) => {
|
|
347
|
-
const newStyle = document.createElement("style");
|
|
348
|
-
newStyle.textContent = style.textContent;
|
|
349
|
-
document.head.appendChild(newStyle);
|
|
350
|
-
});
|
|
351
|
-
// Load scripts
|
|
352
|
-
scripts.forEach((script) => {
|
|
353
|
-
if (script.src) {
|
|
354
|
-
loadScript(script.src);
|
|
355
|
-
} else {
|
|
356
|
-
const newScript = document.createElement("script");
|
|
357
|
-
newScript.textContent = script.textContent;
|
|
358
|
-
document.body.appendChild(newScript);
|
|
359
|
-
document.body.removeChild(newScript);
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
attachWireFunctionEvents();
|
|
364
|
-
}
|
|
365
|
-
function diffAndPatch(oldNode, newNode) {
|
|
366
|
-
// If nodes are of different types, replace old with new
|
|
367
|
-
if (oldNode.nodeType !== newNode.nodeType) {
|
|
368
|
-
oldNode.parentNode?.replaceChild(newNode, oldNode);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
// Compare text nodes
|
|
372
|
-
if (
|
|
373
|
-
oldNode.nodeType === Node.TEXT_NODE &&
|
|
374
|
-
newNode.nodeType === Node.TEXT_NODE
|
|
375
|
-
) {
|
|
376
|
-
if (oldNode.textContent !== newNode.textContent) {
|
|
377
|
-
oldNode.textContent = newNode.textContent;
|
|
378
|
-
}
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
// Compare element nodes
|
|
382
|
-
if (oldNode instanceof HTMLElement && newNode instanceof HTMLElement) {
|
|
383
|
-
oldNode.replaceWith(newNode);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
function loadScript(src) {
|
|
387
|
-
const script = document.createElement("script");
|
|
388
|
-
script.src = src;
|
|
389
|
-
document.head.appendChild(script);
|
|
390
|
-
}
|
|
391
|
-
function saveState() {
|
|
392
|
-
const focusElement = document.activeElement;
|
|
393
|
-
state.focusId = focusElement?.id || focusElement?.name;
|
|
394
|
-
state.focusValue = focusElement?.value;
|
|
395
|
-
state.focusChecked = focusElement?.checked;
|
|
396
|
-
state.focusType = focusElement?.type;
|
|
397
|
-
state.focusSelectionStart = focusElement?.selectionStart;
|
|
398
|
-
state.focusSelectionEnd = focusElement?.selectionEnd;
|
|
399
|
-
state.isSuspense = focusElement.hasAttribute("pp-suspense");
|
|
400
|
-
state.checkedElements.clear();
|
|
401
|
-
document.querySelectorAll('input[type="checkbox"]:checked').forEach((el) => {
|
|
402
|
-
state.checkedElements.add(el.id || el.name);
|
|
403
|
-
});
|
|
404
|
-
document.querySelectorAll('input[type="radio"]:checked').forEach((el) => {
|
|
405
|
-
state.checkedElements.add(el.id || el.name);
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
function restoreState() {
|
|
409
|
-
if (state.focusId) {
|
|
410
|
-
const newFocusElement =
|
|
411
|
-
document.getElementById(state.focusId) ||
|
|
412
|
-
document.querySelector(`[name="${state.focusId}"]`);
|
|
413
|
-
if (newFocusElement instanceof HTMLInputElement) {
|
|
414
|
-
newFocusElement.focus();
|
|
415
|
-
const length = newFocusElement.value.length || 0;
|
|
416
|
-
if (
|
|
417
|
-
state.focusSelectionStart !== undefined &&
|
|
418
|
-
state.focusSelectionEnd !== null
|
|
419
|
-
) {
|
|
420
|
-
newFocusElement.setSelectionRange(length, length);
|
|
421
|
-
}
|
|
422
|
-
if (state.focusValue) {
|
|
423
|
-
if (
|
|
424
|
-
newFocusElement.type === "checkbox" ||
|
|
425
|
-
newFocusElement.type === "radio"
|
|
426
|
-
) {
|
|
427
|
-
newFocusElement.checked = !!state.focusChecked;
|
|
428
|
-
} else {
|
|
429
|
-
const isSuspense = newFocusElement.hasAttribute("pp-suspense");
|
|
430
|
-
if (!isSuspense && !state.isSuspense)
|
|
431
|
-
newFocusElement.value = state.focusValue;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
} else if (newFocusElement instanceof HTMLTextAreaElement) {
|
|
435
|
-
newFocusElement.focus();
|
|
436
|
-
const length = newFocusElement.value.length || 0;
|
|
437
|
-
if (
|
|
438
|
-
state.focusSelectionStart !== undefined &&
|
|
439
|
-
state.focusSelectionEnd !== null
|
|
440
|
-
) {
|
|
441
|
-
newFocusElement.setSelectionRange(length, length);
|
|
442
|
-
}
|
|
443
|
-
if (state.focusValue) {
|
|
444
|
-
newFocusElement.value = state.focusValue;
|
|
445
|
-
}
|
|
446
|
-
} else if (newFocusElement instanceof HTMLSelectElement) {
|
|
447
|
-
newFocusElement.focus();
|
|
448
|
-
if (state.focusValue) {
|
|
449
|
-
newFocusElement.value = state.focusValue;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
state.checkedElements.forEach((id) => {
|
|
454
|
-
const checkbox = document.getElementById(id);
|
|
455
|
-
if (checkbox) {
|
|
456
|
-
checkbox.checked = true;
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
function saveScrollPositions() {
|
|
461
|
-
const scrollPositions = {};
|
|
462
|
-
document.querySelectorAll("*").forEach((el) => {
|
|
463
|
-
if (el.scrollTop || el.scrollLeft) {
|
|
464
|
-
scrollPositions[getElementKey(el)] = {
|
|
465
|
-
scrollTop: el.scrollTop,
|
|
466
|
-
scrollLeft: el.scrollLeft,
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
return scrollPositions;
|
|
471
|
-
}
|
|
472
|
-
function restoreScrollPositions(scrollPositions) {
|
|
473
|
-
document.querySelectorAll("*").forEach((el) => {
|
|
474
|
-
const key = getElementKey(el);
|
|
475
|
-
if (scrollPositions[key]) {
|
|
476
|
-
el.scrollTop = scrollPositions[key].scrollTop;
|
|
477
|
-
el.scrollLeft = scrollPositions[key].scrollLeft;
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
function getElementKey(el) {
|
|
482
|
-
return el.id || el.className || el.tagName;
|
|
483
|
-
}
|
|
484
|
-
async function pphpFetch(url, options) {
|
|
485
|
-
const data = await fetch(url, {
|
|
486
|
-
...options,
|
|
487
|
-
headers: {
|
|
488
|
-
...options?.headers,
|
|
489
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
490
|
-
},
|
|
491
|
-
});
|
|
492
|
-
return await data.text();
|
|
493
|
-
}
|
|
494
|
-
function parseCallback(element, callback) {
|
|
495
|
-
let data = {};
|
|
496
|
-
// Check if the element is inside a form
|
|
497
|
-
const form = element.closest("form");
|
|
498
|
-
if (form) {
|
|
499
|
-
// Serialize the form data
|
|
500
|
-
const formData = new FormData(form);
|
|
501
|
-
formData.forEach((value, key) => {
|
|
502
|
-
if (data[key]) {
|
|
503
|
-
// Handle multiple values
|
|
504
|
-
if (Array.isArray(data[key])) {
|
|
505
|
-
data[key].push(value);
|
|
506
|
-
} else {
|
|
507
|
-
data[key] = [data[key], value];
|
|
508
|
-
}
|
|
509
|
-
} else {
|
|
510
|
-
data[key] = value;
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
} else {
|
|
514
|
-
// Handle single input element
|
|
515
|
-
if (element instanceof HTMLInputElement) {
|
|
516
|
-
data = handleInputElement(element);
|
|
517
|
-
} else if (element instanceof HTMLSelectElement) {
|
|
518
|
-
data[element.name] = element.value;
|
|
519
|
-
} else if (element instanceof HTMLTextAreaElement) {
|
|
520
|
-
data[element.name] = element.value;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
// Parse function name and arguments from the callback string
|
|
524
|
-
const match = callback.match(/(\w+)\((.*)\)/);
|
|
525
|
-
if (match) {
|
|
526
|
-
const funcName = match[1];
|
|
527
|
-
let rawArgs = match[2].trim();
|
|
528
|
-
if (rawArgs.startsWith("{") && rawArgs.endsWith("}")) {
|
|
529
|
-
try {
|
|
530
|
-
const parsedArg = parseJson(rawArgs);
|
|
531
|
-
if (typeof parsedArg === "object" && parsedArg !== null) {
|
|
532
|
-
data = { ...data, ...parsedArg };
|
|
533
|
-
}
|
|
534
|
-
} catch (e) {
|
|
535
|
-
console.error("Error parsing JSON:", e);
|
|
536
|
-
}
|
|
537
|
-
} else {
|
|
538
|
-
const args = rawArgs
|
|
539
|
-
.split(/,(?=(?:[^'"]*['"][^'"]*['"])*[^'"]*$)/)
|
|
540
|
-
.map((arg) => arg.trim().replace(/^['"]|['"]$/g, ""));
|
|
541
|
-
data.args = args;
|
|
542
|
-
}
|
|
543
|
-
return { funcName, data };
|
|
544
|
-
}
|
|
545
|
-
return { funcName: callback, data };
|
|
546
|
-
}
|
|
547
|
-
function handleInputElement(element) {
|
|
548
|
-
let data = {};
|
|
549
|
-
// Only proceed if the element has a name
|
|
550
|
-
if (element.name) {
|
|
551
|
-
// Handle checkboxes
|
|
552
|
-
if (element.type === "checkbox") {
|
|
553
|
-
data[element.name] = {
|
|
554
|
-
value: element.value,
|
|
555
|
-
checked: element.checked,
|
|
556
|
-
};
|
|
557
|
-
} else if (element.type === "radio") {
|
|
558
|
-
// Handle radio buttons
|
|
559
|
-
const checkedRadio = document.querySelector(
|
|
560
|
-
`input[name="${element.name}"]:checked`
|
|
561
|
-
);
|
|
562
|
-
data[element.name] = checkedRadio ? checkedRadio.value : null;
|
|
563
|
-
} else {
|
|
564
|
-
// Handle other input types
|
|
565
|
-
data[element.name] = element.value;
|
|
566
|
-
}
|
|
567
|
-
} else {
|
|
568
|
-
// Handle cases where the element does not have a name
|
|
569
|
-
if (element.type === "checkbox" || element.type === "radio") {
|
|
570
|
-
data.value = element.checked;
|
|
571
|
-
} else {
|
|
572
|
-
data.value = element.value;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
return data;
|
|
576
|
-
}
|
|
577
|
-
function updateElementAttributes(element, data) {
|
|
578
|
-
for (const key in data) {
|
|
579
|
-
if (!data.hasOwnProperty(key)) continue;
|
|
580
|
-
switch (key) {
|
|
581
|
-
case "innerHTML":
|
|
582
|
-
case "outerHTML":
|
|
583
|
-
case "textContent":
|
|
584
|
-
case "innerText":
|
|
585
|
-
element[key] = decodeHTML(data[key]);
|
|
586
|
-
break;
|
|
587
|
-
case "insertAdjacentHTML":
|
|
588
|
-
element.insertAdjacentHTML(
|
|
589
|
-
data.position || "beforeend",
|
|
590
|
-
decodeHTML(data[key].html)
|
|
591
|
-
);
|
|
592
|
-
break;
|
|
593
|
-
case "insertAdjacentText":
|
|
594
|
-
element.insertAdjacentText(
|
|
595
|
-
data.position || "beforeend",
|
|
596
|
-
decodeHTML(data[key].text)
|
|
597
|
-
);
|
|
598
|
-
break;
|
|
599
|
-
case "setAttribute":
|
|
600
|
-
element.setAttribute(data.attrName, decodeHTML(data[key]));
|
|
601
|
-
break;
|
|
602
|
-
case "removeAttribute":
|
|
603
|
-
element.removeAttribute(data[key]);
|
|
604
|
-
break;
|
|
605
|
-
case "className":
|
|
606
|
-
element.className = decodeHTML(data[key]);
|
|
607
|
-
break;
|
|
608
|
-
case "classList.add":
|
|
609
|
-
element.classList.add(...decodeHTML(data[key]).split(","));
|
|
610
|
-
break;
|
|
611
|
-
case "classList.remove":
|
|
612
|
-
element.classList.remove(...decodeHTML(data[key]).split(","));
|
|
613
|
-
break;
|
|
614
|
-
case "classList.toggle":
|
|
615
|
-
element.classList.toggle(decodeHTML(data[key]));
|
|
616
|
-
break;
|
|
617
|
-
case "classList.replace":
|
|
618
|
-
const [oldClass, newClass] = decodeHTML(data[key]).split(",");
|
|
619
|
-
element.classList.replace(oldClass, newClass);
|
|
620
|
-
break;
|
|
621
|
-
case "dataset":
|
|
622
|
-
element.dataset[data.attrName] = decodeHTML(data[key]);
|
|
623
|
-
break;
|
|
624
|
-
case "style":
|
|
625
|
-
Object.assign(element.style, data[key]);
|
|
626
|
-
break;
|
|
627
|
-
case "value":
|
|
628
|
-
element.value = decodeHTML(data[key]);
|
|
629
|
-
break;
|
|
630
|
-
case "checked":
|
|
631
|
-
element.checked = data[key];
|
|
632
|
-
break;
|
|
633
|
-
default:
|
|
634
|
-
element.setAttribute(key, decodeHTML(data[key]));
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
function decodeHTML(html) {
|
|
639
|
-
const txt = document.createElement("textarea");
|
|
640
|
-
txt.innerHTML = html;
|
|
641
|
-
return txt.value;
|
|
642
|
-
}
|
|
643
|
-
async function handleSuspenseElement(element) {
|
|
644
|
-
let suspenseElement = element.getAttribute("pp-suspense") || "";
|
|
645
|
-
const handleFormElement = (form, data) => {
|
|
646
|
-
for (const key in data) {
|
|
647
|
-
if (!data.hasOwnProperty(key)) continue;
|
|
648
|
-
for (const formElement of form.elements) {
|
|
649
|
-
if (
|
|
650
|
-
formElement instanceof HTMLInputElement ||
|
|
651
|
-
formElement instanceof HTMLButtonElement ||
|
|
652
|
-
formElement instanceof HTMLTextAreaElement ||
|
|
653
|
-
formElement instanceof HTMLSelectElement
|
|
654
|
-
) {
|
|
655
|
-
const suspenseElement = formElement.getAttribute("pp-suspense") || "";
|
|
656
|
-
if (suspenseElement) {
|
|
657
|
-
if (isJsonLike(suspenseElement)) {
|
|
658
|
-
const suspenseData = parseJson(suspenseElement);
|
|
659
|
-
if (suspenseData.onsubmit !== "disabled")
|
|
660
|
-
updateElementAttributes(formElement, suspenseData);
|
|
661
|
-
} else {
|
|
662
|
-
updateElementTextContent(formElement, suspenseElement);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
const updateElementTextContent = (element, data) => {
|
|
670
|
-
if (element instanceof HTMLInputElement) {
|
|
671
|
-
element.value = data;
|
|
672
|
-
} else {
|
|
673
|
-
element.textContent = data;
|
|
674
|
-
}
|
|
675
|
-
};
|
|
676
|
-
const handleTargetElement = (element, data) => {
|
|
677
|
-
if (element instanceof HTMLFormElement) {
|
|
678
|
-
handleFormElement(element, data);
|
|
679
|
-
} else {
|
|
680
|
-
updateElementAttributes(element, data);
|
|
681
|
-
}
|
|
682
|
-
};
|
|
683
|
-
try {
|
|
684
|
-
if (suspenseElement && isJsonLike(suspenseElement)) {
|
|
685
|
-
const data = parseJson(suspenseElement);
|
|
686
|
-
if (data) {
|
|
687
|
-
if (element instanceof HTMLFormElement) {
|
|
688
|
-
const formData = new FormData(element);
|
|
689
|
-
const formDataToProcess = {};
|
|
690
|
-
formData.forEach((value, key) => {
|
|
691
|
-
formDataToProcess[key] = value;
|
|
692
|
-
});
|
|
693
|
-
if (data.disabled) {
|
|
694
|
-
toggleFormElements(element, true);
|
|
695
|
-
}
|
|
696
|
-
const { disabled, ...restData } = data;
|
|
697
|
-
updateElementAttributes(element, restData);
|
|
698
|
-
handleFormElement(element, formDataToProcess);
|
|
699
|
-
} else if (data.targets) {
|
|
700
|
-
data.targets.forEach((target) => {
|
|
701
|
-
const { id, ...rest } = target;
|
|
702
|
-
const targetElement = document.querySelector(id);
|
|
703
|
-
if (targetElement) {
|
|
704
|
-
handleTargetElement(targetElement, rest);
|
|
705
|
-
}
|
|
706
|
-
});
|
|
707
|
-
const { targets, ...restData } = data;
|
|
708
|
-
updateElementAttributes(element, restData);
|
|
709
|
-
} else {
|
|
710
|
-
if (data.empty === "disabled" && element.value === "") return;
|
|
711
|
-
const { empty, ...restData } = data;
|
|
712
|
-
updateElementAttributes(element, restData);
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
} else if (suspenseElement) {
|
|
716
|
-
updateElementTextContent(element, suspenseElement);
|
|
717
|
-
} else {
|
|
718
|
-
if (element instanceof HTMLFormElement) {
|
|
719
|
-
const formData = new FormData(element);
|
|
720
|
-
const formDataToProcess = {};
|
|
721
|
-
formData.forEach((value, key) => {
|
|
722
|
-
formDataToProcess[key] = value;
|
|
723
|
-
});
|
|
724
|
-
handleFormElement(element, formDataToProcess);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
} catch (error) {
|
|
728
|
-
console.error("🚀 ~ handleSuspenseElement ~ JSON parse error:", error);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
function parseJson(jsonString) {
|
|
732
|
-
return JSON.parse(jsonString.replace(/'/g, '"'));
|
|
733
|
-
}
|
|
734
|
-
function toggleFormElements(form, disable) {
|
|
735
|
-
Array.from(form.elements).forEach((element) => {
|
|
736
|
-
if (
|
|
737
|
-
element instanceof HTMLInputElement ||
|
|
738
|
-
element instanceof HTMLButtonElement ||
|
|
739
|
-
element instanceof HTMLSelectElement ||
|
|
740
|
-
element instanceof HTMLTextAreaElement
|
|
741
|
-
) {
|
|
742
|
-
element.disabled = disable;
|
|
743
|
-
}
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
async function handleUndefinedFunction(element, funcName, data) {
|
|
747
|
-
const body = {
|
|
748
|
-
callback: funcName,
|
|
749
|
-
...data,
|
|
750
|
-
};
|
|
751
|
-
const firstFetchOptions = {
|
|
752
|
-
method: "POST",
|
|
753
|
-
headers: {
|
|
754
|
-
"Content-Type": "application/json",
|
|
755
|
-
HTTP_PPHP_WIRE_REQUEST: "true",
|
|
756
|
-
},
|
|
757
|
-
body: JSON.stringify(body),
|
|
758
|
-
};
|
|
759
|
-
const secondFetchOptions = {
|
|
760
|
-
method: "POST",
|
|
761
|
-
headers: {
|
|
762
|
-
"Content-Type": "application/json",
|
|
763
|
-
HTTP_PPHP_WIRE_REQUEST: "true",
|
|
764
|
-
},
|
|
765
|
-
body: JSON.stringify({ secondRequestC69CD: true }),
|
|
766
|
-
};
|
|
767
|
-
try {
|
|
768
|
-
handleSuspenseElement(element);
|
|
769
|
-
const targetOnlyAttribute = element.getAttribute("pp-target-only") || "";
|
|
770
|
-
if (targetOnlyAttribute) {
|
|
771
|
-
handlerTargetOnly(element, targetOnlyAttribute);
|
|
772
|
-
}
|
|
773
|
-
const url = window.location.pathname;
|
|
774
|
-
const firstResponseText = await pphpFetch(url, firstFetchOptions);
|
|
775
|
-
if (targetOnlyAttribute) return firstResponseText;
|
|
776
|
-
const functionOnlyAttribute =
|
|
777
|
-
element.getAttribute("pp-function-only") || "";
|
|
778
|
-
handleFunctionOnly(functionOnlyAttribute, firstResponseText);
|
|
779
|
-
if (functionOnlyAttribute) return firstResponseText;
|
|
780
|
-
const secondResponseText = await pphpFetch(url, secondFetchOptions);
|
|
781
|
-
if (firstResponseText.includes("redirect_7F834=")) {
|
|
782
|
-
const url = firstResponseText.split("=")[1];
|
|
783
|
-
await handleRedirect(url);
|
|
784
|
-
} else {
|
|
785
|
-
// Create a DOM parser
|
|
786
|
-
const parser = new DOMParser();
|
|
787
|
-
const doc = parser.parseFromString(secondResponseText, "text/html");
|
|
788
|
-
const bodyContent = doc.body.innerHTML;
|
|
789
|
-
const combinedHTML = firstResponseText + bodyContent;
|
|
790
|
-
updateDocumentContent(combinedHTML);
|
|
791
|
-
}
|
|
792
|
-
} catch (error) {
|
|
793
|
-
console.error("Error:", error);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
function handlerTargetOnly(element, functionOnlyAttribute) {
|
|
797
|
-
if (!isJsonLike(functionOnlyAttribute)) return;
|
|
798
|
-
const getTargetElement = (selector) => {
|
|
799
|
-
if (selector.includes(" + ")) {
|
|
800
|
-
const [baseSelector, siblingSelector] = selector.split(" + ");
|
|
801
|
-
const baseElement = selectElement(baseSelector);
|
|
802
|
-
if (baseElement) {
|
|
803
|
-
const sibling = baseElement.nextElementSibling;
|
|
804
|
-
if (sibling && sibling.matches(siblingSelector)) {
|
|
805
|
-
return sibling;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
return null;
|
|
809
|
-
} else {
|
|
810
|
-
return selectElement(selector);
|
|
811
|
-
}
|
|
812
|
-
};
|
|
813
|
-
const selectElement = (selector) => {
|
|
814
|
-
if (selector.startsWith("#")) {
|
|
815
|
-
const id = selector.slice(1);
|
|
816
|
-
// Use attribute selector for IDs starting with digits
|
|
817
|
-
return document.querySelector(`[id="${id}"]`);
|
|
818
|
-
} else {
|
|
819
|
-
return document.querySelector(selector);
|
|
820
|
-
}
|
|
821
|
-
};
|
|
822
|
-
const applyActions = (target, actions) => {
|
|
823
|
-
actions.forEach((action) => {
|
|
824
|
-
const [method, ...subProps] = action.method.split(".");
|
|
825
|
-
const value = action.value;
|
|
826
|
-
if (subProps.length) {
|
|
827
|
-
let propRef = target[method];
|
|
828
|
-
for (let i = 0; i < subProps.length - 1; i++) {
|
|
829
|
-
propRef = propRef[subProps[i]];
|
|
830
|
-
}
|
|
831
|
-
propRef[subProps[subProps.length - 1]] = value;
|
|
832
|
-
} else {
|
|
833
|
-
target[method] = value;
|
|
834
|
-
}
|
|
835
|
-
});
|
|
836
|
-
};
|
|
837
|
-
const handleClasses = (target, classes, conditionMet) => {
|
|
838
|
-
if (conditionMet) {
|
|
839
|
-
if (classes.add) target.classList.add(...classes.add.split(" "));
|
|
840
|
-
if (classes.remove) target.classList.remove(...classes.remove.split(" "));
|
|
841
|
-
} else {
|
|
842
|
-
if (classes.add) target.classList.remove(...classes.add.split(" "));
|
|
843
|
-
if (classes.remove) target.classList.add(...classes.remove.split(" "));
|
|
844
|
-
}
|
|
845
|
-
};
|
|
846
|
-
const checkCondition = (element, condition) => {
|
|
847
|
-
switch (condition) {
|
|
848
|
-
case "checked":
|
|
849
|
-
return element.checked;
|
|
850
|
-
case "focus":
|
|
851
|
-
return element === document.activeElement;
|
|
852
|
-
// Add more conditions as needed
|
|
853
|
-
default:
|
|
854
|
-
return false;
|
|
855
|
-
}
|
|
856
|
-
};
|
|
857
|
-
const targetOnlyData = parseJson(functionOnlyAttribute);
|
|
858
|
-
targetOnlyData.targets.forEach((targetData) => {
|
|
859
|
-
const targetElement = getTargetElement(targetData.id);
|
|
860
|
-
if (targetElement) {
|
|
861
|
-
const conditionMet = targetData.this.condition
|
|
862
|
-
? checkCondition(element, targetData.this.condition)
|
|
863
|
-
: false;
|
|
864
|
-
const classList = targetData.this.classList;
|
|
865
|
-
const rest = { ...targetData.this };
|
|
866
|
-
if (classList) handleClasses(targetElement, classList, conditionMet);
|
|
867
|
-
Object.keys(rest).forEach((key) => {
|
|
868
|
-
if (key !== "id" && key !== "condition" && key !== "classList") {
|
|
869
|
-
const value = rest[key];
|
|
870
|
-
if (typeof targetElement[key] === "function") {
|
|
871
|
-
targetElement[key](value);
|
|
872
|
-
} else {
|
|
873
|
-
targetElement[key] = value;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
});
|
|
877
|
-
if (targetOnlyData.actions) {
|
|
878
|
-
applyActions(targetElement, targetOnlyData.actions);
|
|
879
|
-
}
|
|
880
|
-
} else {
|
|
881
|
-
console.error(`Invalid selector: ${targetData.id}`);
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
}
|
|
885
|
-
function handleFunctionOnly(functionOnlyAttribute, firstResponseText) {
|
|
886
|
-
if (!isJsonLike(functionOnlyAttribute)) return;
|
|
887
|
-
const functionOnlyData = parseJson(functionOnlyAttribute);
|
|
888
|
-
const responseData = firstResponseText ? parseJson(firstResponseText) : null;
|
|
889
|
-
const targets = functionOnlyData.targets; // Assuming targets is an array of objects
|
|
890
|
-
if (Array.isArray(targets)) {
|
|
891
|
-
targets.forEach((targetData) => {
|
|
892
|
-
const { id, ...restData } = targetData;
|
|
893
|
-
const targetToProcess = document.querySelector(id);
|
|
894
|
-
let targetAttributes = {};
|
|
895
|
-
if (responseData) {
|
|
896
|
-
for (const key in restData) {
|
|
897
|
-
if (restData.hasOwnProperty(key)) {
|
|
898
|
-
switch (key) {
|
|
899
|
-
case "innerHTML":
|
|
900
|
-
case "outerHTML":
|
|
901
|
-
case "textContent":
|
|
902
|
-
case "innerText":
|
|
903
|
-
if (restData[key] === "response") {
|
|
904
|
-
targetAttributes[key] = targetData.responseKey
|
|
905
|
-
? responseData[targetData.responseKey]
|
|
906
|
-
: responseData.response;
|
|
907
|
-
}
|
|
908
|
-
break;
|
|
909
|
-
default:
|
|
910
|
-
targetAttributes[key] = restData[key];
|
|
911
|
-
break;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
} else {
|
|
916
|
-
targetAttributes = restData;
|
|
917
|
-
}
|
|
918
|
-
if (targetToProcess) {
|
|
919
|
-
updateElementAttributes(targetToProcess, targetAttributes);
|
|
920
|
-
}
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
// Function to handle redirection without a full page reload
|
|
925
|
-
async function handleRedirect(url) {
|
|
926
|
-
if (!url) return;
|
|
927
|
-
// Use history.pushState to change the URL without a full refresh
|
|
928
|
-
history.pushState(null, "", url);
|
|
929
|
-
window.dispatchEvent(new PopStateEvent("popstate", { state: null }));
|
|
930
|
-
// Fetch the new content via AJAX
|
|
931
|
-
try {
|
|
932
|
-
const response = await fetch(url, {
|
|
933
|
-
headers: {
|
|
934
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
935
|
-
},
|
|
936
|
-
});
|
|
937
|
-
const data = await response.text();
|
|
938
|
-
updateDocumentContent(data);
|
|
939
|
-
} catch (error) {
|
|
940
|
-
console.error("Redirect error:", error);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
/**
|
|
944
|
-
* Debounces a function to limit the rate at which it is called.
|
|
945
|
-
*
|
|
946
|
-
* The debounced function will postpone its execution until after the specified wait time
|
|
947
|
-
* has elapsed since the last time it was invoked. If `immediate` is `true`, the function
|
|
948
|
-
* will be called at the beginning of the wait period instead of at the end.
|
|
949
|
-
*
|
|
950
|
-
* @param {Function} func - The function to debounce.
|
|
951
|
-
* @param {number} [wait=300] - The number of milliseconds to wait before invoking the function.
|
|
952
|
-
* @param {boolean} [immediate=false] - If `true`, the function is invoked immediately on the leading edge.
|
|
953
|
-
* @returns {Function} - Returns the debounced version of the original function.
|
|
954
|
-
*/
|
|
955
|
-
function debounce(func, wait = 300, immediate = false) {
|
|
956
|
-
let timeout;
|
|
957
|
-
return function (...args) {
|
|
958
|
-
const context = this;
|
|
959
|
-
if (timeout) clearTimeout(timeout);
|
|
960
|
-
timeout = setTimeout(() => {
|
|
961
|
-
timeout = null;
|
|
962
|
-
if (!immediate) func.apply(context, args);
|
|
963
|
-
}, wait);
|
|
964
|
-
if (immediate && !timeout) {
|
|
965
|
-
func.apply(context, args);
|
|
966
|
-
}
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* Copies code to the clipboard and updates the button icon.
|
|
971
|
-
*
|
|
972
|
-
* @param {HTMLElement} btnElement - The button element that triggered the copy action.
|
|
973
|
-
* @param {string} containerClass - The class of the container element that contains the code block.
|
|
974
|
-
* @param {string} initialIconClass - The initial class for the icon.
|
|
975
|
-
* @param {string} successIconClass - The class for the icon after a successful copy.
|
|
976
|
-
* @param {number} [timeout=2000] - The time in milliseconds to revert to the initial icon class.
|
|
977
|
-
*/
|
|
978
|
-
function copyCode(
|
|
979
|
-
btnElement,
|
|
980
|
-
containerClass,
|
|
981
|
-
initialIconClass,
|
|
982
|
-
successIconClass,
|
|
983
|
-
timeout = 2000
|
|
984
|
-
) {
|
|
985
|
-
// Find the closest container with the specified class relative to the button
|
|
986
|
-
const codeBlock = btnElement
|
|
987
|
-
.closest(`.${containerClass}`)
|
|
988
|
-
?.querySelector("pre code");
|
|
989
|
-
const textToCopy = codeBlock?.textContent?.trim() || ""; // Get the text content of the code block and trim whitespace
|
|
990
|
-
if (textToCopy) {
|
|
991
|
-
navigator.clipboard.writeText(textToCopy).then(
|
|
992
|
-
() => {
|
|
993
|
-
// Clipboard successfully set
|
|
994
|
-
const icon = btnElement.querySelector("i");
|
|
995
|
-
if (icon) {
|
|
996
|
-
icon.className = successIconClass; // Change to success icon
|
|
997
|
-
}
|
|
998
|
-
// Set a timeout to change the icon back to initial after the specified timeout
|
|
999
|
-
setTimeout(() => {
|
|
1000
|
-
if (icon) {
|
|
1001
|
-
icon.className = initialIconClass; // Change back to initial icon
|
|
1002
|
-
}
|
|
1003
|
-
}, timeout); // Timeout delay
|
|
1004
|
-
},
|
|
1005
|
-
() => {
|
|
1006
|
-
// Clipboard write failed
|
|
1007
|
-
alert("Failed to copy command to clipboard");
|
|
1008
|
-
}
|
|
1009
|
-
);
|
|
1010
|
-
} else {
|
|
1011
|
-
alert("Failed to find the code block to copy");
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
if (typeof store === "undefined") {
|
|
1015
|
-
class StateManager {
|
|
1016
|
-
static instance = null;
|
|
1017
|
-
state;
|
|
1018
|
-
listeners;
|
|
1019
|
-
/**
|
|
1020
|
-
* Creates a new StateManager instance.
|
|
1021
|
-
*
|
|
1022
|
-
* @param {State} [initialState={}] - The initial state.
|
|
1023
|
-
*/
|
|
1024
|
-
constructor(initialState = {}) {
|
|
1025
|
-
this.state = initialState;
|
|
1026
|
-
this.listeners = [];
|
|
1027
|
-
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Gets the singleton instance of StateManager.
|
|
1030
|
-
*
|
|
1031
|
-
* @param {State} [initialState={}] - The initial state.
|
|
1032
|
-
* @returns {StateManager} - The StateManager instance.
|
|
1033
|
-
*/
|
|
1034
|
-
static getInstance(initialState = {}) {
|
|
1035
|
-
if (!StateManager.instance) {
|
|
1036
|
-
StateManager.instance = new StateManager(initialState);
|
|
1037
|
-
StateManager.instance.loadState(); // Load state immediately after instance creation
|
|
1038
|
-
}
|
|
1039
|
-
return StateManager.instance;
|
|
1040
|
-
}
|
|
1041
|
-
/**
|
|
1042
|
-
* Sets the state.
|
|
1043
|
-
*
|
|
1044
|
-
* @param {Partial<State>} update - The state update.
|
|
1045
|
-
* @param {boolean} [saveToStorage=false] - Whether to save the state to localStorage.
|
|
1046
|
-
*/
|
|
1047
|
-
setState(update, saveToStorage = false) {
|
|
1048
|
-
this.state = { ...this.state, ...update };
|
|
1049
|
-
this.listeners.forEach((listener) => listener(this.state));
|
|
1050
|
-
if (saveToStorage) {
|
|
1051
|
-
this.saveState();
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
/**
|
|
1055
|
-
* Subscribes to state changes.
|
|
1056
|
-
*
|
|
1057
|
-
* @param {(state: State) => void} listener - The listener function.
|
|
1058
|
-
* @returns {() => void} - A function to unsubscribe the listener.
|
|
1059
|
-
*/
|
|
1060
|
-
subscribe(listener) {
|
|
1061
|
-
this.listeners.push(listener);
|
|
1062
|
-
listener(this.state); // Immediately invoke the listener with the current state
|
|
1063
|
-
return () => {
|
|
1064
|
-
this.listeners = this.listeners.filter((l) => l !== listener);
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1067
|
-
/**
|
|
1068
|
-
* Saves the state to localStorage.
|
|
1069
|
-
*/
|
|
1070
|
-
saveState() {
|
|
1071
|
-
localStorage.setItem("appState", JSON.stringify(this.state));
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* Loads the state from localStorage.
|
|
1075
|
-
*/
|
|
1076
|
-
loadState() {
|
|
1077
|
-
const state = localStorage.getItem("appState");
|
|
1078
|
-
if (state) {
|
|
1079
|
-
this.state = JSON.parse(state);
|
|
1080
|
-
this.listeners.forEach((listener) => listener(this.state));
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
/**
|
|
1084
|
-
* Resets the state to its initial value.
|
|
1085
|
-
*
|
|
1086
|
-
* @param {boolean} [clearFromStorage=false] - Whether to clear the state from localStorage.
|
|
1087
|
-
*/
|
|
1088
|
-
resetState(clearFromStorage = false) {
|
|
1089
|
-
this.state = {}; // Reset the state to an empty object or a default state if you prefer
|
|
1090
|
-
this.listeners.forEach((listener) => listener(this.state));
|
|
1091
|
-
if (clearFromStorage) {
|
|
1092
|
-
localStorage.removeItem("appState"); // Clear the state from localStorage
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
store = StateManager.getInstance();
|
|
1097
|
-
}
|
|
1
|
+
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"];document.addEventListener("DOMContentLoaded",attachWireFunctionEvents);var state={checkedElements:new Set},responseData=null,store=null,isNavigating=!1;function attachWireFunctionEvents(){handleHiddenAttribute();document.querySelectorAll("button, input, select, textarea, a, form, label, div, span").forEach((e=>{if(handleAnchorTag(e),eventAttributes.forEach((t=>{const n=e.getAttribute(t),s=t.slice(2);n&&(e.removeAttribute(t),handleDebounce(e,s,n))})),e instanceof HTMLFormElement){const t=e.getAttribute("onsubmit");t&&(e.removeAttribute("onsubmit"),handleDebounce(e,"submit",t))}}))}function handleHiddenAttribute(){document.querySelectorAll("[pp-hidden]").forEach((e=>{let t=e.getAttribute("pp-hidden");if(t)if(isJsonLike(t))try{handleElementVisibility(e,parseJson(t))}catch(e){}else{const n=parseTime(t);n>0&&scheduleVisibilityChange(e,n,"hidden")}}))}function isJsonLike(e){return e.trim().startsWith("{")&&e.trim().endsWith("}")}function handleElementVisibility(e,t){const n=t.start?parseTime(t.start):0,s=t.end?parseTime(t.end):0;n>0?(e.style.visibility="hidden",scheduleVisibilityChange(e,n,"visible"),s>0&&scheduleVisibilityChange(e,n+s,"hidden")):s>0&&scheduleVisibilityChange(e,s,"hidden")}function scheduleVisibilityChange(e,t,n){setTimeout((()=>{requestAnimationFrame((()=>{e.style.visibility=n}))}),t)}function parseTime(e){if("number"==typeof e)return e;const t=e.match(/^(\d+)(ms|s|m)?$/);if(t){const e=parseInt(t[1],10);switch(t[2]||"ms"){case"ms":return e;case"s":return 1e3*e;case"m":return 60*e*1e3;default:return e}}return 0}async function handleDebounce(e,t,n){const s=e.getAttribute("pp-debounce")||"",o=e.getAttribute("pp-trigger")||"",a=e.getAttribute("pp-target-only")||"",c=async t=>{t.preventDefault();try{(o||a)&&(a?invokeHandler(e,o):await invokeHandler(e,o)),await invokeHandler(e,n)}catch(e){}};if(s){const n=debounce(c,parseTime(s));e instanceof HTMLFormElement&&"submit"===t?e.addEventListener(t,(e=>{e.preventDefault(),n(e)})):e.addEventListener(t,n)}else e.addEventListener(t,c)}async function invokeHandler(e,t){try{const n=t.match(/^(\w+(\.\w+)*)\((.*)\)$/);if(n){const s=n[1],o=n[3],a=s.split("."),{context:c,methodName:i}=resolveContext(a);if("function"==typeof c[i]){const e=parseArguments(o);await c[i](...e)}else await handleParsedCallback(e,t)}else await handleParsedCallback(e,t)}catch(e){}}function resolveContext(e){let t=window;for(let n=0;n<e.length-1;n++)if(t=t[e[n]],!t)throw new Error(`Cannot find object ${e[n]} in the context.`);return{context:t,methodName:e[e.length-1]}}function parseArguments(e){return e?JSON.parse(`[${e}]`):[]}async function handleParsedCallback(e,t){const{funcName:n,data:s}=parseCallback(e,t);if(!n)return;const o=window[n];if("function"==typeof o){const t=Array.isArray(s.args)?s.args:[],n=responseData?parseJson(responseData):null;await o(...t,e,s,n)}else responseData=await handleUndefinedFunction(e,n,s)}function handleAnchorTag(e){e instanceof HTMLAnchorElement&&e.addEventListener("click",(async e=>{const t=e.currentTarget,n=t.getAttribute("href"),s=t.getAttribute("target");if(n&&"_blank"!==s&&!e.metaKey&&!e.ctrlKey&&(e.preventDefault(),!isNavigating)){isNavigating=!0;try{/^(https?:)?\/\//i.test(n)&&!n.startsWith(window.location.origin)?window.location.href=n:(history.pushState(null,"",n),await handleNavigation())}catch(e){}finally{isNavigating=!1}}}))}async function handleNavigation(){try{updateDocumentContent(await pphpFetch(window.location.href))}catch(e){}}function updateDocumentContent(e){if(e.includes("<!DOCTYPE html>")){const t=(new DOMParser).parseFromString(e,"text/html"),n=e=>{const t=e.querySelectorAll("script"),n=[];return t.forEach((e=>{const t=document.createElement("script");t.type=e.type||"text/javascript",e.src?t.src=e.src:t.textContent=e.textContent,n.push(t),e.remove()})),n},s=n(t.head),o=n(t.body);document.replaceChild(document.adoptNode(t.documentElement),document.documentElement),s.forEach((e=>document.head.appendChild(e))),o.forEach((e=>document.body.appendChild(e)))}else{saveState();const t=saveScrollPositions(),n=(new DOMParser).parseFromString(e,"text/html"),s=Array.from(n.body.querySelectorAll("script")),o=n.body;diffAndPatch(document.body,o),restoreState(),restoreScrollPositions(t),s.forEach((e=>{const t=document.createElement("script");t.textContent=e.textContent,document.body.appendChild(t),document.body.removeChild(t)}))}attachWireFunctionEvents()}function diffAndPatch(e,t){e.nodeType===t.nodeType?e.nodeType!==Node.TEXT_NODE||t.nodeType!==Node.TEXT_NODE?e instanceof HTMLElement&&t instanceof HTMLElement&&e.replaceWith(t):e.textContent!==t.textContent&&(e.textContent=t.textContent):e.parentNode?.replaceChild(t,e)}function saveState(){const e=document.activeElement;state.focusId=e?.id||e?.name,state.focusValue=e?.value,state.focusChecked=e?.checked,state.focusType=e?.type,state.focusSelectionStart=e?.selectionStart,state.focusSelectionEnd=e?.selectionEnd,state.isSuspense=e.hasAttribute("pp-suspense"),state.checkedElements.clear(),document.querySelectorAll('input[type="checkbox"]:checked').forEach((e=>{state.checkedElements.add(e.id||e.name)})),document.querySelectorAll('input[type="radio"]:checked').forEach((e=>{state.checkedElements.add(e.id||e.name)}))}function restoreState(){if(state.focusId){const e=document.getElementById(state.focusId)||document.querySelector(`[name="${state.focusId}"]`);if(e instanceof HTMLInputElement){e.focus();const t=e.value.length||0;if(void 0!==state.focusSelectionStart&&null!==state.focusSelectionEnd&&e.setSelectionRange(t,t),state.focusValue)if("checkbox"===e.type||"radio"===e.type)e.checked=!!state.focusChecked;else{e.hasAttribute("pp-suspense")||state.isSuspense||(e.value=state.focusValue)}}else if(e instanceof HTMLTextAreaElement){e.focus();const t=e.value.length||0;void 0!==state.focusSelectionStart&&null!==state.focusSelectionEnd&&e.setSelectionRange(t,t),state.focusValue&&(e.value=state.focusValue)}else e instanceof HTMLSelectElement&&(e.focus(),state.focusValue&&(e.value=state.focusValue))}state.checkedElements.forEach((e=>{const t=document.getElementById(e);t&&(t.checked=!0)}))}function saveScrollPositions(){const e={};return document.querySelectorAll("*").forEach((t=>{(t.scrollTop||t.scrollLeft)&&(e[getElementKey(t)]={scrollTop:t.scrollTop,scrollLeft:t.scrollLeft})})),e}function restoreScrollPositions(e){document.querySelectorAll("*").forEach((t=>{const n=getElementKey(t);e[n]&&(t.scrollTop=e[n].scrollTop,t.scrollLeft=e[n].scrollLeft)}))}function getElementKey(e){return e.id||e.className||e.tagName}async function pphpFetch(e,t){const n=await fetch(e,{...t,headers:{...t?.headers,"X-Requested-With":"XMLHttpRequest"}});return await n.text()}function parseCallback(e,t){let n={};const s=e.closest("form");if(s){new FormData(s).forEach(((e,t)=>{n[t]?Array.isArray(n[t])?n[t].push(e):n[t]=[n[t],e]:n[t]=e}))}else e instanceof HTMLInputElement?n=handleInputElement(e):(e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement)&&(n[e.name]=e.value);const o=t.match(/(\w+)\((.*)\)/);if(o){const e=o[1];let t=o[2].trim();if(t.startsWith("{")&&t.endsWith("}"))try{const e=parseJson(t);"object"==typeof e&&null!==e&&(n={...n,...e})}catch(e){}else{const e=t.split(/,(?=(?:[^'"]*['"][^'"]*['"])*[^'"]*$)/).map((e=>e.trim().replace(/^['"]|['"]$/g,"")));n.args=e}return{funcName:e,data:n}}return{funcName:t,data:n}}function handleInputElement(e){let t={};if(e.name)if("checkbox"===e.type)t[e.name]={value:e.value,checked:e.checked};else if("radio"===e.type){const n=document.querySelector(`input[name="${e.name}"]:checked`);t[e.name]=n?n.value:null}else t[e.name]=e.value;else"checkbox"===e.type||"radio"===e.type?t.value=e.checked:t.value=e.value;return t}function updateElementAttributes(e,t){for(const n in t)if(t.hasOwnProperty(n))switch(n){case"innerHTML":case"outerHTML":case"textContent":case"innerText":e[n]=decodeHTML(t[n]);break;case"insertAdjacentHTML":e.insertAdjacentHTML(t.position||"beforeend",decodeHTML(t[n].html));break;case"insertAdjacentText":e.insertAdjacentText(t.position||"beforeend",decodeHTML(t[n].text));break;case"setAttribute":e.setAttribute(t.attrName,decodeHTML(t[n]));break;case"removeAttribute":e.removeAttribute(t[n]);break;case"className":e.className=decodeHTML(t[n]);break;case"classList.add":e.classList.add(...decodeHTML(t[n]).split(","));break;case"classList.remove":e.classList.remove(...decodeHTML(t[n]).split(","));break;case"classList.toggle":e.classList.toggle(decodeHTML(t[n]));break;case"classList.replace":const[s,o]=decodeHTML(t[n]).split(",");e.classList.replace(s,o);break;case"dataset":e.dataset[t.attrName]=decodeHTML(t[n]);break;case"style":Object.assign(e.style,t[n]);break;case"value":e.value=decodeHTML(t[n]);break;case"checked":e.checked=t[n];break;default:e.setAttribute(n,decodeHTML(t[n]))}}function decodeHTML(e){const t=document.createElement("textarea");return t.innerHTML=e,t.value}async function handleSuspenseElement(e){let t=e.getAttribute("pp-suspense")||"";const n=(e,t)=>{for(const n in t)if(t.hasOwnProperty(n))for(const t of e.elements)if(t instanceof HTMLInputElement||t instanceof HTMLButtonElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement){const e=t.getAttribute("pp-suspense")||"";if(e)if(isJsonLike(e)){const n=parseJson(e);"disabled"!==n.onsubmit&&updateElementAttributes(t,n)}else s(t,e)}},s=(e,t)=>{e instanceof HTMLInputElement?e.value=t:e.textContent=t};try{if(t&&isJsonLike(t)){const s=parseJson(t);if(s)if(e instanceof HTMLFormElement){const t=new FormData(e),o={};t.forEach(((e,t)=>{o[t]=e})),s.disabled&&toggleFormElements(e,!0);const{disabled:a,...c}=s;updateElementAttributes(e,c),n(e,o)}else if(s.targets){s.targets.forEach((e=>{const{id:t,...s}=e,o=document.querySelector(t);o&&((e,t)=>{e instanceof HTMLFormElement?n(e,t):updateElementAttributes(e,t)})(o,s)}));const{targets:t,...o}=s;updateElementAttributes(e,o)}else{if("disabled"===s.empty&&""===e.value)return;const{empty:t,...n}=s;updateElementAttributes(e,n)}}else if(t)s(e,t);else if(e instanceof HTMLFormElement){const t=new FormData(e),s={};t.forEach(((e,t)=>{s[t]=e})),n(e,s)}}catch(e){}}function parseJson(e){return JSON.parse(e.replace(/'/g,'"'))}function toggleFormElements(e,t){Array.from(e.elements).forEach((e=>{(e instanceof HTMLInputElement||e instanceof HTMLButtonElement||e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement)&&(e.disabled=t)}))}async function handleUndefinedFunction(e,t,n){const s={callback:t,...n},o={method:"POST",headers:{"Content-Type":"application/json",HTTP_PPHP_WIRE_REQUEST:"true"},body:JSON.stringify(s)},a={method:"POST",headers:{"Content-Type":"application/json",HTTP_PPHP_WIRE_REQUEST:"true"},body:JSON.stringify({secondRequestC69CD:!0})};try{handleSuspenseElement(e);const t=e.getAttribute("pp-target-only")||"";t&&handlerTargetOnly(e,t);const n=window.location.pathname,s=await pphpFetch(n,o);if(t)return s;const c=e.getAttribute("pp-function-only")||"";if(handleFunctionOnly(c,s),c)return s;const i=await pphpFetch(n,a);if(s.includes("redirect_7F834=")){const e=s.split("=")[1];await handleRedirect(e)}else{const e=new DOMParser,t=e.parseFromString(i,"text/html").body.innerHTML;updateDocumentContent(s+t)}}catch(e){}}function handlerTargetOnly(e,t){if(!isJsonLike(t))return;const n=e=>{if(e.startsWith("#")){const t=e.slice(1);return document.querySelector(`[id="${t}"]`)}return document.querySelector(e)},s=parseJson(t);s.targets.forEach((t=>{const o=(e=>{if(e.includes(" + ")){const[t,s]=e.split(" + "),o=n(t);if(o){const e=o.nextElementSibling;if(e&&e.matches(s))return e}return null}return n(e)})(t.id);if(o){const n=!!t.this.condition&&((e,t)=>{switch(t){case"checked":return e.checked;case"focus":return e===document.activeElement;default:return!1}})(e,t.this.condition),c=t.this.classList,i={...t.this};c&&((e,t,n)=>{n?(t.add&&e.classList.add(...t.add.split(" ")),t.remove&&e.classList.remove(...t.remove.split(" "))):(t.add&&e.classList.remove(...t.add.split(" ")),t.remove&&e.classList.add(...t.remove.split(" ")))})(o,c,n),Object.keys(i).forEach((e=>{if("id"!==e&&"condition"!==e&&"classList"!==e){const t=i[e];"function"==typeof o[e]?o[e](t):o[e]=t}})),s.actions&&(a=o,s.actions.forEach((e=>{const[t,...n]=e.method.split("."),s=e.value;if(n.length){let e=a[t];for(let t=0;t<n.length-1;t++)e=e[n[t]];e[n[n.length-1]]=s}else a[t]=s})))}var a}))}function handleFunctionOnly(e,t){if(!isJsonLike(e))return;const n=parseJson(e),s=t?parseJson(t):null,o=n.targets;Array.isArray(o)&&o.forEach((e=>{const{id:t,...n}=e,o=document.querySelector(t);let a={};if(s){for(const t in n)if(n.hasOwnProperty(t))switch(t){case"innerHTML":case"outerHTML":case"textContent":case"innerText":"response"===n[t]&&(a[t]=e.responseKey?s[e.responseKey]:s.response);break;default:a[t]=n[t];break}}else a=n;o&&updateElementAttributes(o,a)}))}async function handleRedirect(e){if(e){history.pushState(null,"",e),window.dispatchEvent(new PopStateEvent("popstate",{state:null}));try{const t=await fetch(e,{headers:{"X-Requested-With":"XMLHttpRequest"}});updateDocumentContent(await t.text())}catch(e){}}}function debounce(e,t=300,n=!1){let s;return function(...o){const a=this;s&&clearTimeout(s),s=setTimeout((()=>{s=null,n||e.apply(a,o)}),t),n&&!s&&e.apply(a,o)}}function copyCode(e,t,n,s,o=2e3){const a=e.closest(`.${t}`)?.querySelector("pre code"),c=a?.textContent?.trim()||"";c?navigator.clipboard.writeText(c).then((()=>{const t=e.querySelector("i");t&&(t.className=s),setTimeout((()=>{t&&(t.className=n)}),o)}),(()=>{alert("Failed to copy command to clipboard")})):alert("Failed to find the code block to copy")}if(window.addEventListener("popstate",(async()=>{await handleNavigation()})),void 0===store){class e{static instance=null;state;listeners;constructor(e={}){this.state=e,this.listeners=[]}static getInstance(t={}){return e.instance||(e.instance=new e(t),e.instance.loadState()),e.instance}setState(e,t=!1){this.state={...this.state,...e},this.listeners.forEach((e=>e(this.state))),t&&this.saveState()}subscribe(e){return this.listeners.push(e),e(this.state),()=>{this.listeners=this.listeners.filter((t=>t!==e))}}saveState(){localStorage.setItem("appState",JSON.stringify(this.state))}loadState(){const e=localStorage.getItem("appState");e&&(this.state=JSON.parse(e),this.listeners.forEach((e=>e(this.state))))}resetState(e=!1){this.state={},this.listeners.forEach((e=>e(this.state))),e&&localStorage.removeItem("appState")}}store=e.getInstance()}
|