create-prisma-php-app 2.0.0-beta.1 → 2.0.0-beta.10
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/composer.json +1 -1
- package/dist/bootstrap.php +0 -1
- package/dist/index.js +4 -8
- package/dist/src/Lib/Auth/Auth.php +15 -8
- package/dist/src/Lib/ErrorHandler.php +3 -0
- package/dist/src/Lib/PHPMailer/Mailer.php +47 -6
- package/dist/src/Lib/PHPX/TwMerge.php +14 -0
- package/dist/src/app/css/tailwind.css +23 -4
- package/dist/src/app/error.php +11 -0
- package/dist/src/app/js/index.js +2 -2
- package/package.json +1 -1
package/composer.json
CHANGED
package/dist/bootstrap.php
CHANGED
|
@@ -627,7 +627,6 @@ final class Bootstrap
|
|
|
627
627
|
|
|
628
628
|
public static function getLoadingsFiles(): string
|
|
629
629
|
{
|
|
630
|
-
// Gather all loading.php files
|
|
631
630
|
$loadingFiles = array_filter(PrismaPHPSettings::$routeFiles, function ($route) {
|
|
632
631
|
$normalizedRoute = str_replace('\\', '/', $route);
|
|
633
632
|
return preg_match('/\/loading\.php$/', $normalizedRoute);
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";import{randomBytes}from"crypto";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let c=`http://localhost/${n}`;c=c.endsWith("/")?c.slice(0,-1):c;const i=c.replace(/(?<!:)(\/\/+)/g,"/"),o=n.replace(/\/\/+/g,"/");return{bsTarget:`${i}/`,bsPathRewrite:{"^/":`/${o.startsWith("/")?o.substring(1):o}/`}}}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8"));n.scripts={...n.scripts,projectName:"tsx settings/project-name.ts"};let c=[];if(s.tailwindcss&&(n.scripts={...n.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch"},c.push("tailwind")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.docker&&(n.scripts={...n.scripts,docker:"docker-compose up"},c.push("docker")),s.swaggerDocs){const e=s.prisma?"tsx settings/auto-swagger-docs.ts":"tsx settings/swagger-config.ts";n.scripts={...n.scripts,"create-swagger-docs":e}}let i={...n.scripts};i.browserSync="tsx settings/bs-config.ts",i.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,n.scripts=i,n.type="module",s.prisma&&(n.prisma={seed:"tsx prisma/seed.ts"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}async function updateComposerJson(e,s){const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require={...n.require,"cboden/ratchet":"^0.4.4"}),s.prisma&&(n.require={...n.require,"calicastle/cuid":"^2.0.0"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nvar ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}function generateAuthSecret(){return randomBytes(33).toString("base64")}function generateLocalStoreKey(){return randomBytes(16).toString("hex")}function copyRecursiveSync(e,s,t){const n=fs.existsSync(e),c=n&&fs.statSync(e);if(n&&c&&c.isDirectory()){const n=s.toLowerCase();if(!t.websocket&&n.includes("src\\lib\\websocket"))return;if(t.backendOnly&&n.includes("src\\app\\js")||t.backendOnly&&n.includes("src\\app\\css")||t.backendOnly&&n.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&n.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((n=>{copyRecursiveSync(path.join(e,n),path.join(s,n),t)}))}else{if(checkExcludeFiles(s))return;if(!t.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!t.websocket&&(s.includes("restart-websocket.ts")||s.includes("restart-websocket.bat")))return;if(!t.docker&&dockerFiles.some((e=>s.includes(e))))return;if(t.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!t.backendOnly&&s.includes("route.php"))return;if(t.backendOnly&&!t.swaggerDocs&&s.includes("layout.php"))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))return;if((!t.swaggerDocs||!t.prisma)&&(s.includes("auto-swagger-docs.ts")||s.includes("prisma-schema-config.json")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,'export default {\n plugins: {\n "@tailwindcss/postcss": {},\n },\n};',{flag:"w"})}function modifyLayoutPHP(e,s){const t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8"),n="";s.backendOnly||(s.tailwindcss||(n='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet" />'),n+='\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet" /> ${n}`:n),e=e.replace("</head>",`${c}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){return!!updateAnswer?.isUpdate&&(updateAnswer?.excludeFilePath?.includes(e.replace(/\\/g,"/"))??!1)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"},{src:"/tsconfig.json",dest:"/tsconfig.json"},{src:"/app-gitignore",dest:"/.gitignore"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.docker&&n.push({src:"/.dockerignore",dest:"/.dockerignore"},{src:"/docker-compose.yml",dest:"/docker-compose.yml"},{src:"/Dockerfile",dest:"/Dockerfile"},{src:"/apache.conf",dest:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),c=path.join(e,t);if(checkExcludeFiles(c))return;const i=fs.readFileSync(n,"utf8");fs.writeFileSync(c,i,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&modifyPostcssConfig(e),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const c=`# Prisma PHP Auth Secret Key\nAUTH_SECRET="${generateAuthSecret()}"\n\n# PHPMailer\n# SMTP_HOST="smtp.gmail.com" or your SMTP host\n# SMTP_USERNAME="john.doe@gmail.com" or your SMTP username\n# SMTP_PASSWORD="123456"\n# SMTP_PORT="587" for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION="ssl" or tls\n# MAIL_FROM="john.doe@gmail.com"\n# MAIL_FROM_NAME="John Doe"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS="true"\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"\n\n# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"\nAPP_ENV="development"\n\n# APP CACHE ENABLED - Set to true to enable caching - Default is false\nCACHE_ENABLED="false"\n# APP CACHE TTL - Set the cache time to live in seconds - Default is 600 seconds (10 minutes)\nCACHE_TTL="600"\n\n# LOCAL STORAGE KEY - Define a custom key for local storage.\n# If not set, it defaults to "pphp_local_store_59e13".\n# Spaces in the value will be replaced with underscores, and the key will be converted to lowercase automatically.\nLOCALSTORE_KEY="${generateLocalStoreKey()}"`;if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${c}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,c)}async function getAnswer(e={}){const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||s.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const t=()=>{process.exit(0)},n=await prompts(s,{onCancel:t}),c=[];n.backendOnly||e.backendOnly?(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||c.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const i=await prompts(c,{onCancel:t});return{projectName:n.projectName?String(n.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:n.backendOnly??e.backendOnly??!1,swaggerDocs:i.swaggerDocs??e.swaggerDocs??!1,tailwindcss:i.tailwindcss??e.tailwindcss??!1,websocket:i.websocket??e.websocket??!1,prisma:i.prisma??e.prisma??!1,docker:i.docker??e.docker??!1}}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const t=e.split(".").map(Number),n=s.split(".").map(Number);for(let e=0;e<t.length;e++){if(t[e]>n[e])return 1;if(t[e]<n[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}
|
|
2
|
+
import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";import{randomBytes}from"crypto";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php","error.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let c=`http://localhost/${n}`;c=c.endsWith("/")?c.slice(0,-1):c;const o=c.replace(/(?<!:)(\/\/+)/g,"/"),i=n.replace(/\/\/+/g,"/");return{bsTarget:`${o}/`,bsPathRewrite:{"^/":`/${i.startsWith("/")?i.substring(1):i}/`}}}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8"));n.scripts={...n.scripts,projectName:"tsx settings/project-name.ts"};let c=[];if(s.tailwindcss&&(n.scripts={...n.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch"},c.push("tailwind")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.docker&&(n.scripts={...n.scripts,docker:"docker-compose up"},c.push("docker")),s.swaggerDocs){const e=s.prisma?"tsx settings/auto-swagger-docs.ts":"tsx settings/swagger-config.ts";n.scripts={...n.scripts,"create-swagger-docs":e}}let o={...n.scripts};o.browserSync="tsx settings/bs-config.ts",o.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,n.scripts=o,n.type="module",s.prisma&&(n.prisma={seed:"tsx prisma/seed.ts"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}async function updateComposerJson(e,s){const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require={...n.require,"cboden/ratchet":"^0.4.4"}),s.prisma&&(n.require={...n.require,"calicastle/cuid":"^2.0.0"}),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nvar ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}function generateAuthSecret(){return randomBytes(33).toString("base64")}function generateLocalStoreKey(){return randomBytes(16).toString("hex")}function copyRecursiveSync(e,s,t){const n=fs.existsSync(e),c=n&&fs.statSync(e);if(n&&c&&c.isDirectory()){const n=s.toLowerCase();if(!t.websocket&&n.includes("src\\lib\\websocket"))return;if(t.backendOnly&&n.includes("src\\app\\js")||t.backendOnly&&n.includes("src\\app\\css")||t.backendOnly&&n.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&n.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((n=>{copyRecursiveSync(path.join(e,n),path.join(s,n),t)}))}else{if(checkExcludeFiles(s))return;if(!t.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!t.websocket&&(s.includes("restart-websocket.ts")||s.includes("restart-websocket.bat")))return;if(!t.docker&&dockerFiles.some((e=>s.includes(e))))return;if(t.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!t.backendOnly&&s.includes("route.php"))return;if(t.backendOnly&&!t.swaggerDocs&&s.includes("layout.php"))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))return;if((!t.swaggerDocs||!t.prisma)&&(s.includes("auto-swagger-docs.ts")||s.includes("prisma-schema-config.json")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,'export default {\n plugins: {\n "@tailwindcss/postcss": {},\n cssnano: {},\n },\n};',{flag:"w"})}function modifyLayoutPHP(e,s){const t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8"),n="";s.backendOnly||(s.tailwindcss||(n='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet" />'),n+='\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet" /> ${n}`:n),e=e.replace("</head>",`${c}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){return!!updateAnswer?.isUpdate&&(updateAnswer?.excludeFilePath?.includes(e.replace(/\\/g,"/"))??!1)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"},{src:"/tsconfig.json",dest:"/tsconfig.json"},{src:"/app-gitignore",dest:"/.gitignore"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.docker&&n.push({src:"/.dockerignore",dest:"/.dockerignore"},{src:"/docker-compose.yml",dest:"/docker-compose.yml"},{src:"/Dockerfile",dest:"/Dockerfile"},{src:"/apache.conf",dest:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),c=path.join(e,t);if(checkExcludeFiles(c))return;const o=fs.readFileSync(n,"utf8");fs.writeFileSync(c,o,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&modifyPostcssConfig(e),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const c=`# Prisma PHP Auth Secret Key\nAUTH_SECRET="${generateAuthSecret()}"\n\n# PHPMailer\n# SMTP_HOST="smtp.gmail.com" or your SMTP host\n# SMTP_USERNAME="john.doe@gmail.com" or your SMTP username\n# SMTP_PASSWORD="123456"\n# SMTP_PORT="587" for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION="ssl" or tls\n# MAIL_FROM="john.doe@gmail.com"\n# MAIL_FROM_NAME="John Doe"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS="true"\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"\n\n# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"\nAPP_ENV="development"\n\n# APP CACHE ENABLED - Set to true to enable caching - Default is false\nCACHE_ENABLED="false"\n# APP CACHE TTL - Set the cache time to live in seconds - Default is 600 seconds (10 minutes)\nCACHE_TTL="600"\n\n# LOCAL STORAGE KEY - Define a custom key for local storage.\n# If not set, it defaults to "pphp_local_store_59e13".\n# Spaces in the value will be replaced with underscores, and the key will be converted to lowercase automatically.\nLOCALSTORE_KEY="${generateLocalStoreKey()}"`;if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${c}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,c)}async function getAnswer(e={}){const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||s.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const t=()=>{process.exit(0)},n=await prompts(s,{onCancel:t}),c=[];n.backendOnly||e.backendOnly?(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||c.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,active:"Yes",inactive:"No"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const o=await prompts(c,{onCancel:t});return{projectName:n.projectName?String(n.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:n.backendOnly??e.backendOnly??!1,swaggerDocs:o.swaggerDocs??e.swaggerDocs??!1,tailwindcss:o.tailwindcss??e.tailwindcss??!1,websocket:o.websocket??e.websocket??!1,prisma:o.prisma??e.prisma??!1,docker:o.docker??e.docker??!1}}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const t=e.split(".").map(Number),n=s.split(".").map(Number);for(let e=0;e<t.length;e++){if(t[e]>n[e])return 1;if(t[e]<n[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}
|
|
3
3
|
/**
|
|
4
4
|
* Install dependencies in the specified directory.
|
|
5
5
|
* @param {string} baseDir - The base directory where to install the dependencies.
|
|
@@ -32,13 +32,10 @@ async function installDependencies(baseDir, dependencies, isDev = false) {
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
const pinnedVersions = {
|
|
35
|
-
"@
|
|
36
|
-
"@prisma/internals": "^6.4.1",
|
|
37
|
-
"@tailwindcss/postcss": "^4.0.9",
|
|
35
|
+
"@tailwindcss/postcss": "^4.0.12",
|
|
38
36
|
"@types/browser-sync": "^2.29.0",
|
|
39
|
-
"@types/node": "^22.13.
|
|
37
|
+
"@types/node": "^22.13.10",
|
|
40
38
|
"@types/prompts": "^2.4.9",
|
|
41
|
-
autoprefixer: "^10.4.20",
|
|
42
39
|
"browser-sync": "^3.0.3",
|
|
43
40
|
chalk: "^5.4.1",
|
|
44
41
|
"chokidar-cli": "^3.0.0",
|
|
@@ -48,9 +45,8 @@ const pinnedVersions = {
|
|
|
48
45
|
"php-parser": "^3.2.2",
|
|
49
46
|
postcss: "^8.5.3",
|
|
50
47
|
"postcss-cli": "^11.0.0",
|
|
51
|
-
prisma: "^6.4.1",
|
|
52
48
|
prompts: "^2.4.2",
|
|
53
|
-
tailwindcss: "^4.0.
|
|
49
|
+
tailwindcss: "^4.0.12",
|
|
54
50
|
tsx: "^4.19.3",
|
|
55
51
|
typescript: "^5.8.2",
|
|
56
52
|
};
|
|
@@ -12,6 +12,9 @@ use Lib\Validator;
|
|
|
12
12
|
use GuzzleHttp\Client;
|
|
13
13
|
use GuzzleHttp\Exception\RequestException;
|
|
14
14
|
use Lib\Request;
|
|
15
|
+
use Exception;
|
|
16
|
+
use InvalidArgumentException;
|
|
17
|
+
use ArrayObject;
|
|
15
18
|
|
|
16
19
|
class Auth
|
|
17
20
|
{
|
|
@@ -70,14 +73,14 @@ class Auth
|
|
|
70
73
|
* try {
|
|
71
74
|
* $jwt = $auth->signIn('Admin', '1h');
|
|
72
75
|
* echo "JWT: " . $jwt;
|
|
73
|
-
* } catch (
|
|
76
|
+
* } catch (InvalidArgumentException $e) {
|
|
74
77
|
* echo "Error: " . $e->getMessage();
|
|
75
78
|
* }
|
|
76
79
|
*/
|
|
77
80
|
public function signIn($data, ?string $tokenValidity = null): string
|
|
78
81
|
{
|
|
79
82
|
if (!$this->secretKey) {
|
|
80
|
-
throw new
|
|
83
|
+
throw new InvalidArgumentException("Secret key is required for authentication.");
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
$expirationTime = $this->calculateExpirationTime($tokenValidity ?? $this->defaultTokenValidity);
|
|
@@ -131,6 +134,10 @@ class Auth
|
|
|
131
134
|
return false;
|
|
132
135
|
}
|
|
133
136
|
|
|
137
|
+
if (!isset($_SESSION[self::PAYLOAD_SESSION_KEY])) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
134
141
|
return true;
|
|
135
142
|
}
|
|
136
143
|
|
|
@@ -158,11 +165,11 @@ class Auth
|
|
|
158
165
|
case 'd':
|
|
159
166
|
return new DateInterval("P{$value}D");
|
|
160
167
|
default:
|
|
161
|
-
throw new
|
|
168
|
+
throw new InvalidArgumentException("Invalid duration format: {$duration}");
|
|
162
169
|
}
|
|
163
170
|
}
|
|
164
171
|
|
|
165
|
-
throw new
|
|
172
|
+
throw new InvalidArgumentException("Invalid duration format: {$duration}");
|
|
166
173
|
}
|
|
167
174
|
|
|
168
175
|
/**
|
|
@@ -190,7 +197,7 @@ class Auth
|
|
|
190
197
|
}
|
|
191
198
|
|
|
192
199
|
return $token;
|
|
193
|
-
} catch (
|
|
200
|
+
} catch (Exception) {
|
|
194
201
|
return null;
|
|
195
202
|
}
|
|
196
203
|
}
|
|
@@ -215,7 +222,7 @@ class Auth
|
|
|
215
222
|
$decodedToken = $this->verifyToken($jwt);
|
|
216
223
|
|
|
217
224
|
if (!$decodedToken) {
|
|
218
|
-
throw new
|
|
225
|
+
throw new InvalidArgumentException("Invalid token.");
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
$expirationTime = $this->calculateExpirationTime($tokenValidity ?? $this->defaultTokenValidity);
|
|
@@ -282,7 +289,7 @@ class Auth
|
|
|
282
289
|
{
|
|
283
290
|
if (isset($_SESSION[self::PAYLOAD_SESSION_KEY])) {
|
|
284
291
|
$value = $_SESSION[self::PAYLOAD_SESSION_KEY][self::PAYLOAD_NAME];
|
|
285
|
-
return is_array($value) ? new
|
|
292
|
+
return is_array($value) ? new ArrayObject($value, ArrayObject::ARRAY_AS_PROPS) : $value;
|
|
286
293
|
}
|
|
287
294
|
|
|
288
295
|
return null;
|
|
@@ -317,7 +324,7 @@ class Auth
|
|
|
317
324
|
private function findProvider(array $providers, string $type): ?object
|
|
318
325
|
{
|
|
319
326
|
foreach ($providers as $provider) {
|
|
320
|
-
if ($provider
|
|
327
|
+
if (is_object($provider) && get_class($provider) === $type) {
|
|
321
328
|
return $provider;
|
|
322
329
|
}
|
|
323
330
|
}
|
|
@@ -38,7 +38,8 @@ class Mailer
|
|
|
38
38
|
* @param string $to The recipient's email address.
|
|
39
39
|
* @param string $subject The subject of the email.
|
|
40
40
|
* @param string $body The HTML body of the email.
|
|
41
|
-
* @param array $options (optional) Additional email options like name, altBody, CC, and
|
|
41
|
+
* @param array $options (optional) Additional email options like name, altBody, CC, BCC, and attachments.
|
|
42
|
+
* - attachments: A string or an array of file paths, or an array of associative arrays with keys 'path' and 'name'.
|
|
42
43
|
*
|
|
43
44
|
* @return bool Returns true if the email is sent successfully, false otherwise.
|
|
44
45
|
*
|
|
@@ -50,7 +51,7 @@ class Mailer
|
|
|
50
51
|
// Validate and sanitize inputs
|
|
51
52
|
$to = Validator::email($to);
|
|
52
53
|
if (!$to) {
|
|
53
|
-
throw new
|
|
54
|
+
throw new Exception('Invalid email address for the main recipient');
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
$subject = Validator::string($subject);
|
|
@@ -60,6 +61,7 @@ class Mailer
|
|
|
60
61
|
$name = $options['name'] ?? '';
|
|
61
62
|
$addCC = $options['addCC'] ?? [];
|
|
62
63
|
$addBCC = $options['addBCC'] ?? [];
|
|
64
|
+
$attachments = $options['attachments'] ?? [];
|
|
63
65
|
|
|
64
66
|
$name = Validator::string($name);
|
|
65
67
|
|
|
@@ -67,6 +69,10 @@ class Mailer
|
|
|
67
69
|
$this->handleRecipients($addCC, 'CC');
|
|
68
70
|
// Handle BCC recipients
|
|
69
71
|
$this->handleRecipients($addBCC, 'BCC');
|
|
72
|
+
// Handle file attachments if provided
|
|
73
|
+
if (!empty($attachments)) {
|
|
74
|
+
$this->handleAttachments($attachments);
|
|
75
|
+
}
|
|
70
76
|
|
|
71
77
|
// Set the main recipient and other email properties
|
|
72
78
|
$this->mail->addAddress($to, $name);
|
|
@@ -77,8 +83,8 @@ class Mailer
|
|
|
77
83
|
|
|
78
84
|
// Send the email
|
|
79
85
|
return $this->mail->send();
|
|
80
|
-
} catch (
|
|
81
|
-
throw new
|
|
86
|
+
} catch (Exception $e) {
|
|
87
|
+
throw new Exception($e->getMessage());
|
|
82
88
|
}
|
|
83
89
|
}
|
|
84
90
|
|
|
@@ -101,7 +107,7 @@ class Mailer
|
|
|
101
107
|
if ($recipient) {
|
|
102
108
|
$this->mail->{$method}($recipient);
|
|
103
109
|
} else {
|
|
104
|
-
throw new
|
|
110
|
+
throw new Exception("Invalid email address in $type");
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
113
|
} else {
|
|
@@ -109,12 +115,47 @@ class Mailer
|
|
|
109
115
|
if ($recipient) {
|
|
110
116
|
$this->mail->{$method}($recipient);
|
|
111
117
|
} else {
|
|
112
|
-
throw new
|
|
118
|
+
throw new Exception("Invalid email address in $type");
|
|
113
119
|
}
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
}
|
|
117
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Handle adding file attachments.
|
|
126
|
+
*
|
|
127
|
+
* @param string|array $attachments File path(s) to attach.
|
|
128
|
+
* You can pass a string for a single file or an array of file paths.
|
|
129
|
+
* Alternatively, each attachment can be an array with keys 'path' and 'name' for custom naming.
|
|
130
|
+
*
|
|
131
|
+
* @throws Exception Throws an exception if any attachment file is not found.
|
|
132
|
+
*/
|
|
133
|
+
private function handleAttachments(string|array $attachments): void
|
|
134
|
+
{
|
|
135
|
+
if (is_array($attachments)) {
|
|
136
|
+
foreach ($attachments as $attachment) {
|
|
137
|
+
if (is_array($attachment)) {
|
|
138
|
+
$file = $attachment['path'] ?? null;
|
|
139
|
+
$name = $attachment['name'] ?? '';
|
|
140
|
+
if (!$file || !file_exists($file)) {
|
|
141
|
+
throw new Exception("Attachment file does not exist: " . ($file ?? 'unknown'));
|
|
142
|
+
}
|
|
143
|
+
$this->mail->addAttachment($file, $name);
|
|
144
|
+
} else {
|
|
145
|
+
if (!file_exists($attachment)) {
|
|
146
|
+
throw new Exception("Attachment file does not exist: $attachment");
|
|
147
|
+
}
|
|
148
|
+
$this->mail->addAttachment($attachment);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
if (!file_exists($attachments)) {
|
|
153
|
+
throw new Exception("Attachment file does not exist: $attachments");
|
|
154
|
+
}
|
|
155
|
+
$this->mail->addAttachment($attachments);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
118
159
|
/**
|
|
119
160
|
* Convert HTML content to plain text.
|
|
120
161
|
*
|
|
@@ -167,6 +167,20 @@ class TwMerge
|
|
|
167
167
|
$splitClasses = preg_split("/\s+/", $item);
|
|
168
168
|
foreach ($splitClasses as $individualClass) {
|
|
169
169
|
$classKey = self::getClassGroup($individualClass);
|
|
170
|
+
|
|
171
|
+
// If the class is non-responsive (no colon), remove any responsive variants for the same base
|
|
172
|
+
if (strpos($classKey, ':') === false) {
|
|
173
|
+
// The base group is the class key itself (e.g. "justify")
|
|
174
|
+
$baseGroup = $classKey;
|
|
175
|
+
// Remove any entries that end with the same base group but have a prefix (e.g. "sm:justify")
|
|
176
|
+
foreach ($classArray as $existingKey => $existingClass) {
|
|
177
|
+
if ($existingKey !== $baseGroup && substr($existingKey, -strlen($baseGroup)) === $baseGroup) {
|
|
178
|
+
unset($classArray[$existingKey]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Get the conflicting keys and remove them
|
|
170
184
|
$conflictingKeys = self::getConflictingKeys($classKey);
|
|
171
185
|
|
|
172
186
|
// Remove any conflicting classes
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
@import "tailwindcss" source("../../../src/");
|
|
2
2
|
|
|
3
|
+
:root {
|
|
4
|
+
--background: #ffffff;
|
|
5
|
+
--foreground: #171717;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@theme inline {
|
|
9
|
+
--color-background: var(--background);
|
|
10
|
+
--color-foreground: var(--foreground);
|
|
11
|
+
--font-sans: var(--font-geist-sans);
|
|
12
|
+
--font-mono: var(--font-geist-mono);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@media (prefers-color-scheme: dark) {
|
|
16
|
+
:root {
|
|
17
|
+
--background: #0a0a0a;
|
|
18
|
+
--foreground: #ededed;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
3
22
|
@layer base {
|
|
4
23
|
/* Define fonts */
|
|
5
24
|
@font-face {
|
|
@@ -12,13 +31,13 @@
|
|
|
12
31
|
src: url("../assets/fonts/GeistMonoVF.woff") format("woff");
|
|
13
32
|
}
|
|
14
33
|
|
|
15
|
-
/* Global styles */
|
|
16
34
|
body {
|
|
17
|
-
|
|
35
|
+
background: var(--background);
|
|
36
|
+
color: var(--foreground);
|
|
37
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
18
38
|
}
|
|
19
39
|
|
|
20
|
-
/* GeistMonoVF for code elements */
|
|
21
40
|
code {
|
|
22
|
-
font-family: "GeistMonoVF", monospace;
|
|
41
|
+
font-family: "GeistMonoVF", monospace;
|
|
23
42
|
}
|
|
24
43
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?php use Lib\ErrorHandler; ?>
|
|
2
|
+
|
|
3
|
+
<div class="flex items-center justify-center h-screen">
|
|
4
|
+
<div class="text-center max-w-md">
|
|
5
|
+
<h1 class="text-6xl font-bold text-red-500">Oops!</h1>
|
|
6
|
+
<p class="text-xl mt-4"><?= ErrorHandler::$content ?></p>
|
|
7
|
+
<div class="mt-6">
|
|
8
|
+
<a href="/" class="px-6 py-3 text-lg font-semibold bg-red-500 hover:bg-red-600 rounded-lg shadow-md">Go Back Home</a>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
package/dist/src/app/js/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(()=>{const e=EventTarget.prototype.addEventListener,t=EventTarget.prototype.removeEventListener,n=new Map;EventTarget.prototype.addEventListener=function(t,s,i){n.has(this)||n.set(this,new Map);const a=n.get(this).get(t)||new Set;a.add(s),n.get(this).set(t,a),e.call(this,t,s,i)},EventTarget.prototype.removeEventListener=function(e,s,i){if(n.has(this)&&n.get(this).has(e)){const t=n.get(this).get(e);t&&(t.delete(s),0===t.size&&n.get(this).delete(e))}t.call(this,e,s,i)},EventTarget.prototype.removeAllEventListeners=function(e){if(n.has(this)&&n.get(this).has(e)){const s=n.get(this).get(e);s&&(s.forEach((n=>{t.call(this,e,n)})),n.get(this).delete(e))}}})();class PPHP{static _instance;eventHandlers;redirectRegex=/redirect_7F834\s*=\s*(\/[^\s]*)/;isNavigating=!1;responseData=null;state={checkedElements:new Set};constructor(){this.eventHandlers=new Set(["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"]),this.handlePopState()}static get instance(){return PPHP._instance||(PPHP._instance=new PPHP),PPHP._instance}handlePopState(){window.addEventListener("popstate",(async()=>{await this.handleNavigation()}))}attachWireFunctionEvents(){this.handleHiddenAttribute();document.querySelectorAll("button, input, select, textarea, a, form, label, div, span").forEach((e=>{if(this.handleAnchorTag(e),Array.from(e.attributes).filter((e=>this.eventHandlers.has(e.name))).forEach((t=>{const n=t.name.slice(2),s=t.value;e instanceof HTMLInputElement&&this.handleInputAppendParams(e,n),s&&(e.removeAttribute(t.name),this.handleDebounce(e,n,s))})),e instanceof HTMLFormElement){const t=e.getAttribute("onsubmit");t&&(e.removeAttribute("onsubmit"),this.handleDebounce(e,"submit",t))}}))}async handleDebounce(e,t,n){e.removeEventListener(t,(()=>{}));const s=e.getAttribute("pp-debounce")||"",i=e.getAttribute("pp-before-request")||"",a=e.getAttribute("pp-after-request")||"",o=async t=>{t.preventDefault();try{i&&await this.invokeHandler(e,i,t),await this.invokeHandler(e,n,t),a&&"@close"!==a&&await this.invokeHandler(e,a,t),this.handlerAutofocusAttribute()}catch(e){}};if(s){const n=this.parseTime(s),i=this.debounce(o,n);e instanceof HTMLFormElement&&"submit"===t?e.addEventListener(t,(e=>{e.preventDefault(),i(e)})):e.addEventListener(t,i)}else e.addEventListener(t,o)}debounce(e,t=300,n=!1){let s;return function(...i){const a=this;s&&clearTimeout(s),s=setTimeout((()=>{s=null,n||e.apply(a,i)}),t),n&&!s&&e.apply(a,i)}}handlerAutofocusAttribute(){const e=document.querySelectorAll("[pp-autofocus]");let t=!1;e.forEach((e=>{if(t)return;const n=e.getAttribute("pp-autofocus");if(!n||!this.isJsonLike(n))return;const s=this.parseJson(n);if(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement){const t=["text","search","tel","url","password"];if(e instanceof HTMLInputElement)if(t.includes(e.type))if("number"===e.type){e.type="text";const t=e.value.length||0;e.setSelectionRange(t,t),e.type="number"}else this.setCursorPosition(e,s);else;else e instanceof HTMLTextAreaElement&&this.setCursorPosition(e,s)}e.focus(),t=!0}))}async invokeHandler(e,t,n){try{const s=t.match(/^(\w+(\.\w+)*)\((.*)\)$/);if(s){const i=s[1],a=s[3],o=i.split("."),{context:r,methodName:c}=this.resolveContext(o);if("function"==typeof r[c])if(this.isJsonLike(a)){const t=this.parseJson(a);t.element=e,t.event=n;const s=[t];await r[c](...s)}else new Function("event",t).call(e,n);else await this.handleParsedCallback(e,t)}else await this.handleParsedCallback(e,t)}catch(e){}}async handleParsedCallback(e,t){const{funcName:n,data:s}=this.parseCallback(e,t);if(!n)return;const i=window[n];if("function"==typeof i){const t=e.hasAttribute("pp-after-request"),n=Array.isArray(s.args)?s.args:[],a=this.responseData?this.parseJson(this.responseData):{response:this.responseData};let o={args:n,element:e,data:s};t&&(o={...o,...a}),await i(o)}else this.responseData=null,this.responseData=await this.handleUndefinedFunction(e,n,s)}async handleUndefinedFunction(e,t,n){const s={callback:t,...n},i=this.createFetchOptions(s),a=this.createFetchOptions({secondRequestC69CD:!0,...this.getUrlParams()});try{this.saveElementOriginalState(e),this.handleSuspenseElement(e);const n=new URL(window.location.href);let s="",o="",r={success:!1};const c=e.querySelector("input[type='file']");if(c){if(s=await this.fetchFile(n.href,t,c),o=this.extractJson(s)||"",o)try{r=this.parseJson(o)}catch(e){}}else{const e=await this.fetch(n.href,i);if(s=await e.text(),this.getRedirectUrl(s))return void await this.handleRedirect(this.getRedirectUrl(s)||"");if(o=this.extractJson(s)||"",o)try{r=this.parseJson(o)}catch(e){}}const l=e.getAttribute("pp-before-request")||"",h=e.getAttribute("pp-after-request")||"";if((l||h&&r.success)&&this.restoreSuspenseElement(e),l||h){let e="";if(r.success){e=s.replace(o,"")}else e=s;if(this.appendAfterbegin(e),!h&&!r.success)return}if(h&&r.success){this.handleAfterRequest(h,o);const e=s.replace(o,"");return this.appendAfterbegin(e),o}if("@close"===h)return r.success?o:void 0;const d=await this.fetch(n.href,a),u=await d.text();if(this.getRedirectUrl(u))return void await this.handleRedirect(this.getRedirectUrl(u)||"");await this.handleResponseRedirectOrUpdate(s,u,o,r)}catch(e){}}handleAfterRequest(e,t){if(!this.isJsonLike(e))return;const n=this.parseJson(e),s=t?this.parseJson(t):null,i=n.targets;Array.isArray(i)&&i.forEach((e=>{const{id:t,...n}=e,i=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;i&&this.updateElementAttributes(i,a)}))}async handleResponseRedirectOrUpdate(e,t,n,s){const i=this.getUpdatedHTMLContent(e,n,s),a=(new DOMParser).parseFromString(t,"text/html");i&&a.body.insertAdjacentElement("afterbegin",i),this.updateBodyContent(a.body.outerHTML)}getUpdatedHTMLContent(e,t,n){const s=document.createElement("div");if(s.id="afterbegin-8D95D",n&&t?.success){const t=e.replace(n,"");s.innerHTML=t}else s.innerHTML=e;return s.innerHTML?s:null}async updateBodyContent(e){const t=this.saveScrollPositions();this.saveState();const n=(new DOMParser).parseFromString(e,"text/html");document.removeAllEventListeners("PPBodyLoaded"),await this.appendCallbackResponse(n),this.restoreState(),this.restoreScrollPositions(t),this.attachWireFunctionEvents(),document.dispatchEvent(new Event("PPBodyLoaded"))}restoreState(){if(this.state.focusId){const e=document.getElementById(this.state.focusId)||document.querySelector(`[name="${this.state.focusId}"]`);if(e instanceof HTMLInputElement){const t=e.value.length||0;void 0!==this.state.focusSelectionStart&&null!==this.state.focusSelectionEnd&&e.setSelectionRange(t,t),this.state.focusValue&&("checkbox"===e.type||"radio"===e.type?e.checked=!!this.state.focusChecked:"number"===e.type||"email"===e.type?(e.type="text",e.setSelectionRange(t,t),e.type="number"===e.type?"number":"email"):"date"===e.type||"month"===e.type||"week"===e.type||"time"===e.type||"datetime-local"===e.type||"color"===e.type||"file"===e.type||""!==e.value&&(e.value=this.state.focusValue)),e.focus()}else if(e instanceof HTMLTextAreaElement){const t=e.value.length||0;void 0!==this.state.focusSelectionStart&&null!==this.state.focusSelectionEnd&&e.setSelectionRange(t,t),this.state.focusValue&&""!==e.value&&(e.value=this.state.focusValue),e.focus()}else e instanceof HTMLSelectElement&&(this.state.focusValue&&""!==e.value&&(e.value=this.state.focusValue),e.focus())}this.state.checkedElements.forEach((e=>{const t=document.getElementById(e);t&&(t.checked=!0)}))}async appendCallbackResponse(e){const t=e.getElementById("afterbegin-8D95D");if(t){const e=document.getElementById("afterbegin-8D95D");e?e.innerHTML=t.innerHTML:document.body.insertAdjacentHTML("afterbegin",t.outerHTML)}await this.populateDocumentBody(e)}saveState(){const e=document.activeElement;this.state.focusId=e?.id||e?.name,this.state.focusValue=e?.value,this.state.focusChecked=e?.checked,this.state.focusType=e?.type,this.state.focusSelectionStart=e?.selectionStart,this.state.focusSelectionEnd=e?.selectionEnd,this.state.isSuspense=e.hasAttribute("pp-suspense"),this.state.checkedElements.clear(),document.querySelectorAll('input[type="checkbox"]:checked').forEach((e=>{this.state.checkedElements.add(e.id||e.name)})),document.querySelectorAll('input[type="radio"]:checked').forEach((e=>{this.state.checkedElements.add(e.id||e.name)}))}updateElementAttributes(e,t){for(const n in t)if(t.hasOwnProperty(n))switch(n){case"innerHTML":case"outerHTML":case"textContent":case"innerText":e[n]=this.decodeHTML(t[n]);break;case"insertAdjacentHTML":e.insertAdjacentHTML(t.position||"beforeend",this.decodeHTML(t[n].html));break;case"insertAdjacentText":e.insertAdjacentText(t.position||"beforeend",this.decodeHTML(t[n].text));break;case"setAttribute":e.setAttribute(t.attrName,this.decodeHTML(t[n]));break;case"removeAttribute":e.removeAttribute(t[n]);break;case"className":e.className=this.decodeHTML(t[n]);break;case"classList.add":e.classList.add(...this.decodeHTML(t[n]).split(","));break;case"classList.remove":e.classList.remove(...this.decodeHTML(t[n]).split(","));break;case"classList.toggle":e.classList.toggle(this.decodeHTML(t[n]));break;case"classList.replace":const[s,i]=this.decodeHTML(t[n]).split(",");e.classList.replace(s,i);break;case"dataset":e.dataset[t.attrName]=this.decodeHTML(t[n]);break;case"style":Object.assign(e.style,t[n]);break;case"value":e.value=this.decodeHTML(t[n]);break;case"checked":e.checked=t[n];break;default:e.setAttribute(n,this.decodeHTML(t[n]))}}decodeHTML(e){const t=document.createElement("textarea");return t.innerHTML=e,t.value}appendAfterbegin(e){if(!e)return;const t="afterbegin-8D95D";let n=document.getElementById(t);n?(n.innerHTML=e,document.body.insertAdjacentElement("afterbegin",n)):(n=document.createElement("div"),n.id=t,n.innerHTML=e,document.body.insertAdjacentElement("afterbegin",n))}restoreSuspenseElement(e){const t=e.getAttribute("pp-original-state");if(e.hasAttribute("pp-suspense")&&t){const n=(e,t)=>{for(const n in t)t.hasOwnProperty(n)&&("textContent"===n?e.textContent=t[n]:"innerHTML"===n?e.innerHTML=t[n]:"disabled"===n?!0===t[n]?e.setAttribute("disabled","true"):e.removeAttribute("disabled"):e.setAttribute(n,t[n]));for(const n of Array.from(e.attributes))t.hasOwnProperty(n.name)||e.removeAttribute(n.name)},s=(e,t)=>{for(const s in t)if(t.hasOwnProperty(s))for(const t of Array.from(e.elements))if(t instanceof HTMLInputElement||t instanceof HTMLButtonElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement){const e=t.getAttribute("pp-original-state")||"";if(e){if(this.isJsonLike(e)){const s=this.parseJson(e);n(t,s)}else i(t,e);t.removeAttribute("pp-original-state")}}},i=(e,t)=>{e instanceof HTMLInputElement?e.value=t:e.textContent=t},a=(e,t)=>{e instanceof HTMLFormElement?s(e,t):n(e,t)};try{const i=this.parseJson(t);if(i)if(e instanceof HTMLFormElement){const t=e.id;if(t){const e=document.querySelector(`[form="${t}"]`);if(e){const t=e.getAttribute("pp-original-state");if(t&&this.isJsonLike(t)){const s=this.parseJson(t);n(e,s)}}}const i=new FormData(e),a={};if(i.forEach(((e,t)=>{a[t]=e})),s(e,a),e.hasAttribute("pp-suspense")){const t=e.getAttribute("pp-suspense")||"";if(this.parseJson(t).disabled)for(const t of Array.from(e.elements))(t instanceof HTMLInputElement||t instanceof HTMLButtonElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement)&&t.removeAttribute("disabled")}}else if(i.targets){i.targets.forEach((e=>{const{id:t,...n}=e,s=document.querySelector(t);s&&a(s,n)}));const{targets:t,...s}=i;n(e,s)}else{const{empty:t,...s}=i;n(e,s)}}catch(e){}}e.querySelectorAll("[pp-suspense]").forEach((e=>this.restoreSuspenseElement(e))),e.removeAttribute("pp-original-state")}extractJson(e){const t=e?.match(/\{[\s\S]*\}/);return t?t[0]:null}getRedirectUrl(e){const t=e.match(this.redirectRegex);return t?t[1]:null}async fetchFile(e,t,n){const s=new FormData,i=n.files;if(i)for(let e=0;e<i.length;e++)s.append("file[]",i[e]);s.append("callback",t);const a=await fetch(e,{method:"POST",headers:{HTTP_PPHP_WIRE_REQUEST:"true"},body:s});return await a.text()}async 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(this.isJsonLike(e)){const n=this.parseJson(e);"disabled"!==n.onsubmit&&this.updateElementAttributes(t,n),n.targets&&n.targets.forEach((e=>{const{id:t,...n}=e,s=document.querySelector(t);s&&i(s,n)}))}else s(t,e)}},s=(e,t)=>{e instanceof HTMLInputElement?e.value=t:e.textContent=t},i=(e,t)=>{e instanceof HTMLFormElement?n(e,t):this.updateElementAttributes(e,t)};try{if(t&&this.isJsonLike(t)){const s=this.parseJson(t);if(s)if(e instanceof HTMLFormElement){const t=new FormData(e),i={};t.forEach(((e,t)=>{i[t]=e})),s.disabled&&this.toggleFormElements(e,!0);const{disabled:a,...o}=s;this.updateElementAttributes(e,o),n(e,i)}else if(s.targets){s.targets.forEach((e=>{const{id:t,...n}=e,s=document.querySelector(t);s&&i(s,n)}));const{targets:t,...n}=s;this.updateElementAttributes(e,n)}else{if("disabled"===s.empty&&""===e.value)return;const{empty:t,...n}=s;this.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){}}toggleFormElements(e,t){Array.from(e.elements).forEach((e=>{(e instanceof HTMLInputElement||e instanceof HTMLButtonElement||e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement)&&(e.disabled=t)}))}saveElementOriginalState(e){if(e.hasAttribute("pp-suspense")&&!e.hasAttribute("pp-original-state")){const t={};e.textContent&&(t.textContent=e.textContent.trim()),e.innerHTML&&(t.innerHTML=e.innerHTML.trim()),(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement)&&(t.value=e.value);for(let n=0;n<e.attributes.length;n++){const s=e.attributes[n];t[s.name]=s.value}e.setAttribute("pp-original-state",JSON.stringify(t))}if(e instanceof HTMLFormElement){let t=null;const n=e.id;n&&(t=document.querySelector(`[form="${n}"]`)),n&&t||(t=Array.from(e.elements).find((e=>e instanceof HTMLButtonElement||e instanceof HTMLInputElement))),t&&(t.hasAttribute("pp-original-state")||this.saveElementOriginalState(t))}e.querySelectorAll("[pp-suspense]").forEach((e=>this.saveElementOriginalState(e)))}getUrlParams(){const e={};return new URLSearchParams(window.location.search).forEach(((t,n)=>{e[n]=t})),e}createFetchOptions(e){return{method:"POST",headers:{"Content-Type":"application/json",HTTP_PPHP_WIRE_REQUEST:"true"},body:JSON.stringify(e)}}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=this.handleInputElement(e):(e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement)&&(n[e.name]=e.value);const i=t.match(/(\w+)\((.*)\)/);if(i){const e=i[1];let t=i[2].trim();if(t.startsWith("{")&&t.endsWith("}"))try{const e=this.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}}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}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]}}setCursorPosition(e,t){if(t.start)e.setSelectionRange(0,0);else if(t.end){const t=e.value.length||0;e.setSelectionRange(t,t)}else if(t.length){const n=parseInt(t.length,10)||0;e.setSelectionRange(n,n)}}handleInputAppendParams(e,t){"true"===e.getAttribute("pp-append-params")&&e.addEventListener(t,(e=>{const t=e.currentTarget,n=t.value.trim(),s=new URL(window.location.href),i=new URLSearchParams(s.search),a=t.name;if(a){n?i.set(a,n):i.delete(a);const e=i.toString()?`${s.pathname}?${i.toString()}`:s.pathname;history.replaceState(null,"",e)}}))}handleHiddenAttribute(){const e=document.querySelectorAll("[pp-visibility]"),t=document.querySelectorAll("[pp-display]");e.forEach((e=>this.handleVisibilityElementAttribute(e,"pp-visibility",this.handleElementVisibility))),t.forEach((e=>this.handleVisibilityElementAttribute(e,"pp-display",this.handleElementDisplay)))}handleVisibilityElementAttribute(e,t,n){const s=e.getAttribute(t);if(s)if(this.isJsonLike(s)){n(e,this.parseJson(s))}else{const n=this.parseTime(s);if(n>0){const s="pp-visibility"===t?"visibility":"display",i="visibility"===s?"hidden":"none";this.scheduleChange(e,n,s,i)}}}handleElementVisibility(e,t){this.handleElementChange(e,t,"visibility","hidden","visible")}handleElementDisplay(e,t){this.handleElementChange(e,t,"display","none","block")}handleElementChange(e,t,n,s,i){const a=t.start?this.parseTime(t.start):0,o=t.end?this.parseTime(t.end):0;a>0?(e.style[n]=s,this.scheduleChange(e,a,n,i),o>0&&this.scheduleChange(e,a+o,n,s)):o>0&&this.scheduleChange(e,o,n,s)}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(),!this.isNavigating)){this.isNavigating=!0;try{if(/^(https?:)?\/\//i.test(n)&&!n.startsWith(window.location.origin))window.location.href=n;else{const e=t.getAttribute("pp-append-params");if(n.startsWith("?")&&"true"===e){const e=new URL(window.location.href),t=new URLSearchParams(e.search);let s="";const[i,a]=n.split("#");a&&(s=`#${a}`);new URLSearchParams(i.split("?")[1]).forEach(((e,n)=>{t.set(n,e)}));const o=`${e.pathname}?${t.toString()}${s}`;history.pushState(null,"",o)}else{const[e,t]=n.split("#"),s=`${e}${t?`#${t}`:""}`;history.pushState(null,"",s)}const s=n.indexOf("#");if(-1!==s){const e=n.slice(s+1),t=document.getElementById(e);if(t)t.scrollIntoView({behavior:"smooth"});else{await this.handleNavigation();const t=document.getElementById(e);t&&t.scrollIntoView({behavior:"smooth"})}}else await this.handleNavigation()}}catch(e){}finally{this.isNavigating=!1}}}))}async handleNavigation(){try{const e=e=>{const t=e.querySelector("[pp-loading-transition]")?.getAttribute("pp-loading-transition");let n=250,s=250;if(t)try{const e=this.parseJson(t);n=this.parseTime(e.fadeIn||n),s=this.parseTime(e.fadeOut||s)}catch(e){}return{fadeIn:n,fadeOut:s}},t=(e,t)=>new Promise((n=>{e.style.transition=`opacity ${t}ms ease-out`,e.style.opacity="0",setTimeout((()=>{e.style.transition="",n()}),t)})),n=(e,t)=>{e.style.transition=`opacity ${t}ms ease-in`,e.style.opacity="1",setTimeout((()=>{e.style.transition=""}),t)},s=async s=>{const i=document.querySelector("[pp-loading-content='true']")||document.body;if(i){const{fadeIn:a,fadeOut:o}=e(s);await t(i,o),i.innerHTML=s.innerHTML,n(i,a)}},i=window.location.pathname,a=document.getElementById("loading-file-1B87E");if(a){let e=null,t=i;for(;t&&(e=a.querySelector(`div[pp-loading-url='${t}']`),!e);){const e=t.lastIndexOf("/");t=e>0?t.substring(0,e):"/"}e||(e=a.querySelector("div[pp-loading-url='/']")),e&&await s(e)}const o=await this.fetch(window.location.href),r=await o.text(),c=r.match(this.redirectRegex);if(c&&c[1]){const e=c[1];await this.handleRedirect(e)}else this.updateDocumentContent(r)}catch(e){}}async updateDocumentContent(e){const t=this.saveScrollPositions(),n=(new DOMParser).parseFromString(e,"text/html"),s="pp-dynamic-script",i="pp-dynamic-link";document.head.querySelectorAll("[pp-dynamic-meta]").forEach((e=>e.remove()));document.head.querySelectorAll("[pp-dynamic-link]").forEach((e=>e.remove()));document.head.querySelectorAll("[pp-dynamic-script]").forEach((e=>e.remove()));document.removeAllEventListeners("PPBodyLoaded"),await(async e=>{Array.from(e.head.children).forEach((e=>{const t=e.tagName;if("SCRIPT"===t&&e.hasAttribute(s)){const t=document.createElement("script");Array.from(e.attributes).forEach((e=>t.setAttribute(e.name,e.value))),e.textContent&&(t.textContent=e.textContent),document.head.appendChild(t)}else if("META"===t){if(e.getAttribute("charset")||"viewport"===e.getAttribute("name"))return;const t=e.name,n=e.getAttribute("property"),s=document.head.querySelector(t?`meta[name="${t}"]`:`meta[property="${n}"]`),i=document.head.querySelector("title");s?document.head.replaceChild(e.cloneNode(!0),s):i?.nextSibling?document.head.insertBefore(e.cloneNode(!0),i.nextSibling):document.head.appendChild(e.cloneNode(!0))}else if("TITLE"===t){const t=document.head.querySelector("title");t?document.head.replaceChild(e.cloneNode(!0),t):document.head.appendChild(e.cloneNode(!0))}else if("LINK"===t){const t=t=>{const n=document.head.querySelector('link[rel="icon"]');if(n)document.head.replaceChild(e.cloneNode(!0),n);else{const e=document.createElement("link");e.rel="icon",e.href=t,document.head.appendChild(e)}};if("icon"===e.getAttribute("rel")){t(e.href)}else if(e.hasAttribute(i)){const t=e.cloneNode(!0);document.head.appendChild(t)}}})),await this.populateDocumentBody(e)})(n),this.restoreScrollPositions(t),this.attachWireFunctionEvents(),document.dispatchEvent(new Event("PPBodyLoaded"))}restoreScrollPositions(e){requestAnimationFrame((()=>{const t=e.window;t&&window.scrollTo(t.scrollLeft,t.scrollTop),document.querySelectorAll("*").forEach((t=>{const n=this.getElementKey(t);e[n]&&(t.scrollTop=e[n].scrollTop,t.scrollLeft=e[n].scrollLeft)}))}))}async populateDocumentBody(e){try{const t=e.body.cloneNode(!0);this.manageScriptTags(t),document.body.replaceWith(t)}catch(e){}}manageScriptTags(e,t){const n=e.querySelectorAll("script"),s=t?.querySelectorAll("script")||n;n.forEach(((e,t)=>{const n=document.createElement("script"),i=s[t]||e;Array.from(i.attributes).forEach((e=>{n.setAttribute(e.name,e.value)})),i.hasAttribute("src")||(n.textContent=i.textContent),e.parentNode?.replaceChild(n,e)}))}saveScrollPositions(){const e={window:{scrollTop:window.scrollY||document.documentElement.scrollTop,scrollLeft:window.scrollX||document.documentElement.scrollLeft}};return document.querySelectorAll("*").forEach((t=>{(t.scrollTop||t.scrollLeft)&&(e[this.getElementKey(t)]={scrollTop:t.scrollTop,scrollLeft:t.scrollLeft})})),e}getElementKey(e){return e.id||e.className||e.tagName}async handleRedirect(e){if(e)try{const t=new URL(e,window.location.origin);t.origin!==window.location.origin?window.location.href=e:(history.pushState(null,"",e),await this.handleNavigation())}catch(e){}}async fetch(e,t){return fetch(e,{...t,headers:{...t?.headers,"X-Requested-With":"XMLHttpRequest"}})}isJsonLike(e){return"string"==typeof e&&((e=e.trim()).startsWith("{")&&e.endsWith("}")||e.startsWith("[")&&e.endsWith("]"))}parseJson(e){try{return JSON5.parse(e)}catch(e){return null}}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}scheduleChange(e,t,n,s){setTimeout((()=>{requestAnimationFrame((()=>{e.style[n]=s}))}),t)}observeDOMChanges(){new MutationObserver((e=>{for(const t of e)"childList"===t.type&&t.addedNodes.length>0&&this.attachWireFunctionEvents()})).observe(document.body,{childList:!0,subtree:!0})}async fetchFunction(e,t={}){try{const n={callback:e,...t},s=this.createFetchOptions(n),i=await this.fetch(window.location.href,s);if(!i.ok)throw new Error(`Fetch failed with status: ${i.status} ${i.statusText}`);const a=await i.text();try{return JSON.parse(a)}catch{return a}}catch(e){throw new Error("Failed to fetch data.")}}async sync(...e){try{const t=e.length>0?e.map((e=>`pp-sync="${e}"`)):['pp-sync="true"'],n=this.createFetchOptions({secondRequestC69CD:!0,...this.getUrlParams()}),s=await this.fetch(window.location.href,n),i=await s.text(),a=(new DOMParser).parseFromString(i,"text/html");t.forEach((e=>{const t=document.querySelectorAll(`[${e}]`),n=a.body.querySelectorAll(`[${e}]`);t.forEach(((e,t)=>{const s=n[t];s&&(e.innerHTML=s.innerHTML,this.reRunScripts(e))}))}))}catch(e){}}async fetchAndUpdateBodyContent(){const e=this.createFetchOptions({secondRequestC69CD:!0,...this.getUrlParams()}),t=await this.fetch(window.location.href,e),n=await t.text();await this.updateBodyContent(n)}reRunScripts(e){e.querySelectorAll("script").forEach((e=>{const t=document.createElement("script");Array.from(e.attributes).forEach((e=>{t.setAttribute(e.name,e.value)})),e.hasAttribute("src")||(t.textContent=e.textContent),e.parentNode?.replaceChild(t,e)}))}copyCode(e,t,n,s,i="img",a=2e3){if(!(e instanceof HTMLElement))return;const o=e.closest(`.${t}`)?.querySelector("pre code"),r=o?.textContent?.trim()||"";r?navigator.clipboard.writeText(r).then((()=>{const t=e.querySelector(i);if(t)for(const[e,n]of Object.entries(s))e in t?t[e]=n:t.setAttribute(e,n);setTimeout((()=>{if(t)for(const[e,s]of Object.entries(n))e in t?t[e]=s:t.setAttribute(e,s)}),a)}),(()=>{alert("Failed to copy command to clipboard")})):alert("Failed to find the code block to copy")}getCookie(e){return document.cookie.split("; ").find((t=>t.startsWith(e+"=")))?.split("=")[1]||null}}class PPHPLocalStore{static instance=null;state;listeners;pphp;STORAGE_KEY;constructor(e={}){this.state=e,this.listeners=[],this.pphp=PPHP.instance,this.STORAGE_KEY=this.pphp.getCookie("pphp_local_store_key")||"pphp_local_store_59e13",this.loadState()}static getInstance(e={}){return PPHPLocalStore.instance||(PPHPLocalStore.instance=new PPHPLocalStore(e)),PPHPLocalStore.instance}setState(e,t=!1){if(this.state={...this.state,...e},this.listeners.forEach((e=>e(this.state))),this.saveState(),t){const e=localStorage.getItem(this.STORAGE_KEY);e&&this.pphp.fetchFunction(this.STORAGE_KEY,{[this.STORAGE_KEY]:e})}}saveState(){localStorage.setItem(this.STORAGE_KEY,JSON.stringify(this.state))}loadState(){const e=localStorage.getItem(this.STORAGE_KEY);e&&(this.state=this.pphp.parseJson(e),this.listeners.forEach((e=>e(this.state))))}resetState(e,t=!1){if(e?(delete this.state[e],this.saveState()):(this.state={},localStorage.removeItem(this.STORAGE_KEY)),this.listeners.forEach((e=>e(this.state))),t){const t=e?localStorage.getItem(this.STORAGE_KEY):null;this.pphp.fetchFunction(this.STORAGE_KEY,{[this.STORAGE_KEY]:t})}}}
|
|
1
|
+
(()=>{const e=EventTarget.prototype.addEventListener,t=EventTarget.prototype.removeEventListener,n=new Map;EventTarget.prototype.addEventListener=function(t,s,i){n.has(this)||n.set(this,new Map);const a=n.get(this).get(t)||new Set;a.add(s),n.get(this).set(t,a),e.call(this,t,s,i)},EventTarget.prototype.removeEventListener=function(e,s,i){if(n.has(this)&&n.get(this).has(e)){const t=n.get(this).get(e);t&&(t.delete(s),0===t.size&&n.get(this).delete(e))}t.call(this,e,s,i)},EventTarget.prototype.removeAllEventListeners=function(e){if(n.has(this)&&n.get(this).has(e)){const s=n.get(this).get(e);s&&(s.forEach((n=>{t.call(this,e,n)})),n.get(this).delete(e))}}})();class PPHP{static _instance;eventHandlers;redirectRegex=/redirect_7F834\s*=\s*(\/[^\s]*)/;isNavigating=!1;responseData=null;state={checkedElements:new Set};constructor(){this.eventHandlers=new Set(["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"]),this.handlePopState()}static get instance(){return PPHP._instance||(PPHP._instance=new PPHP),PPHP._instance}handlePopState(){window.addEventListener("popstate",(async()=>{await this.handleNavigation()}))}attachWireFunctionEvents(){this.handleHiddenAttribute();document.querySelectorAll("button, input, select, textarea, a, form, label, div, span").forEach((e=>{if(this.handleAnchorTag(e),Array.from(e.attributes).filter((e=>this.eventHandlers.has(e.name))).forEach((t=>{const n=t.name.slice(2),s=t.value;e instanceof HTMLInputElement&&this.handleInputAppendParams(e,n),s&&(e.removeAttribute(t.name),this.handleDebounce(e,n,s))})),e instanceof HTMLFormElement){const t=e.getAttribute("onsubmit");t&&(e.removeAttribute("onsubmit"),this.handleDebounce(e,"submit",t))}}))}async handleDebounce(e,t,n){e.removeEventListener(t,(()=>{}));const s=e.getAttribute("pp-debounce")||"",i=e.getAttribute("pp-before-request")||"",a=e.getAttribute("pp-after-request")||"",o=async t=>{t.preventDefault();try{i&&await this.invokeHandler(e,i,t),await this.invokeHandler(e,n,t),a&&"@close"!==a&&await this.invokeHandler(e,a,t),this.handlerAutofocusAttribute()}catch(e){}};if(s){const n=this.parseTime(s),i=this.debounce(o,n);e instanceof HTMLFormElement&&"submit"===t?e.addEventListener(t,(e=>{e.preventDefault(),i(e)})):e.addEventListener(t,i)}else e.addEventListener(t,o)}debounce(e,t=300,n=!1){let s;return function(...i){const a=this;s&&clearTimeout(s),s=setTimeout((()=>{s=null,n||e.apply(a,i)}),t),n&&!s&&e.apply(a,i)}}handlerAutofocusAttribute(){const e=document.querySelectorAll("[pp-autofocus]");let t=!1;e.forEach((e=>{if(t)return;const n=e.getAttribute("pp-autofocus");if(!n||!this.isJsonLike(n))return;const s=this.parseJson(n);if(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement){const t=["text","search","tel","url","password"];if(e instanceof HTMLInputElement)if(t.includes(e.type))if("number"===e.type){e.type="text";const t=e.value.length||0;e.setSelectionRange(t,t),e.type="number"}else this.setCursorPosition(e,s);else;else e instanceof HTMLTextAreaElement&&this.setCursorPosition(e,s)}e.focus(),t=!0}))}async invokeHandler(e,t,n){try{const s=t.match(/^(\w+(\.\w+)*)\((.*)\)$/);if(s){const i=s[1],a=s[3],o=i.split("."),{context:r,methodName:c}=this.resolveContext(o);if("function"==typeof r[c])if(this.isJsonLike(a)){const t=this.parseJson(a);t.element=e,t.event=n;const s=[t];await r[c](...s)}else new Function("event",t).call(e,n);else await this.handleParsedCallback(e,t)}else await this.handleParsedCallback(e,t)}catch(e){}}async handleParsedCallback(e,t){const{funcName:n,data:s}=this.parseCallback(e,t);if(!n)return;const i=this[n],a=window[n];let o;if("function"==typeof i?o=i:"function"==typeof a&&(o=a),"function"==typeof o){const t=e.hasAttribute("pp-after-request"),n=Array.isArray(s.args)?s.args:[],i=this.responseData?this.parseJson(this.responseData):{response:this.responseData};let a={args:n,element:e,data:s};t&&(a={...a,...i}),await o.call(this,a)}else this.responseData=null,this.responseData=await this.handleUndefinedFunction(e,n,s)}async handleUndefinedFunction(e,t,n){const s={callback:t,...n},i=this.createFetchOptions(s),a=this.createFetchOptions({secondRequestC69CD:!0,...this.getUrlParams()});try{this.saveElementOriginalState(e),this.handleSuspenseElement(e);const n=new URL(window.location.href);let s="",o="",r={success:!1};const c=e.querySelector("input[type='file']");if(c){if(s=await this.fetchFile(n.href,t,c),o=this.extractJson(s)||"",o)try{r=this.parseJson(o)}catch(e){}}else{const e=await this.fetch(n.href,i);if(s=await e.text(),this.getRedirectUrl(s))return void await this.handleRedirect(this.getRedirectUrl(s)||"");if(o=this.extractJson(s)||"",o)try{r=this.parseJson(o)}catch(e){}}const l=e.getAttribute("pp-before-request")||"",h=e.getAttribute("pp-after-request")||"";if((l||h&&r.success)&&this.restoreSuspenseElement(e),l||h){let e="";if(r.success){e=s.replace(o,"")}else e=s;if(this.appendAfterbegin(e),!h&&!r.success)return}if(h&&r.success){this.handleAfterRequest(h,o);const e=s.replace(o,"");return this.appendAfterbegin(e),o}if("@close"===h)return r.success?o:void 0;const d=await this.fetch(n.href,a),u=await d.text();if(this.getRedirectUrl(u))return void await this.handleRedirect(this.getRedirectUrl(u)||"");await this.handleResponseRedirectOrUpdate(s,u,o,r)}catch(e){}}handleAfterRequest(e,t){if(!this.isJsonLike(e))return;const n=this.parseJson(e),s=t?this.parseJson(t):null,i=n.targets;Array.isArray(i)&&i.forEach((e=>{const{id:t,...n}=e,i=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;i&&this.updateElementAttributes(i,a)}))}async handleResponseRedirectOrUpdate(e,t,n,s){const i=this.getUpdatedHTMLContent(e,n,s),a=(new DOMParser).parseFromString(t,"text/html");i&&a.body.insertAdjacentElement("afterbegin",i),this.updateBodyContent(a.body.outerHTML)}getUpdatedHTMLContent(e,t,n){const s=document.createElement("div");if(s.id="afterbegin-8D95D",n&&t?.success){const t=e.replace(n,"");s.innerHTML=t}else s.innerHTML=e;return s.innerHTML?s:null}async updateBodyContent(e){const t=this.saveScrollPositions();this.saveState();const n=(new DOMParser).parseFromString(e,"text/html");document.removeAllEventListeners("PPBodyLoaded"),await this.appendCallbackResponse(n),this.restoreState(),this.restoreScrollPositions(t),this.attachWireFunctionEvents(),document.dispatchEvent(new Event("PPBodyLoaded"))}restoreState(){if(this.state.focusId){const e=document.getElementById(this.state.focusId)||document.querySelector(`[name="${this.state.focusId}"]`);if(e instanceof HTMLInputElement){const t=e.value.length||0;void 0!==this.state.focusSelectionStart&&null!==this.state.focusSelectionEnd&&e.setSelectionRange(t,t),this.state.focusValue&&("checkbox"===e.type||"radio"===e.type?e.checked=!!this.state.focusChecked:"number"===e.type||"email"===e.type?(e.type="text",e.setSelectionRange(t,t),e.type="number"===e.type?"number":"email"):"date"===e.type||"month"===e.type||"week"===e.type||"time"===e.type||"datetime-local"===e.type||"color"===e.type||"file"===e.type||""!==e.value&&(e.value=this.state.focusValue)),e.focus()}else if(e instanceof HTMLTextAreaElement){const t=e.value.length||0;void 0!==this.state.focusSelectionStart&&null!==this.state.focusSelectionEnd&&e.setSelectionRange(t,t),this.state.focusValue&&""!==e.value&&(e.value=this.state.focusValue),e.focus()}else e instanceof HTMLSelectElement&&(this.state.focusValue&&""!==e.value&&(e.value=this.state.focusValue),e.focus())}this.state.checkedElements.forEach((e=>{const t=document.getElementById(e);t&&(t.checked=!0)}))}async appendCallbackResponse(e){const t=e.getElementById("afterbegin-8D95D");if(t){const e=document.getElementById("afterbegin-8D95D");e?e.innerHTML=t.innerHTML:document.body.insertAdjacentHTML("afterbegin",t.outerHTML)}await this.populateDocumentBody(e)}saveState(){const e=document.activeElement;this.state.focusId=e?.id||e?.name,this.state.focusValue=e?.value,this.state.focusChecked=e?.checked,this.state.focusType=e?.type,this.state.focusSelectionStart=e?.selectionStart,this.state.focusSelectionEnd=e?.selectionEnd,this.state.isSuspense=e.hasAttribute("pp-suspense"),this.state.checkedElements.clear(),document.querySelectorAll('input[type="checkbox"]:checked').forEach((e=>{this.state.checkedElements.add(e.id||e.name)})),document.querySelectorAll('input[type="radio"]:checked').forEach((e=>{this.state.checkedElements.add(e.id||e.name)}))}updateElementAttributes(e,t){for(const n in t)if(t.hasOwnProperty(n))switch(n){case"innerHTML":case"outerHTML":case"textContent":case"innerText":e[n]=this.decodeHTML(t[n]);break;case"insertAdjacentHTML":e.insertAdjacentHTML(t.position||"beforeend",this.decodeHTML(t[n].html));break;case"insertAdjacentText":e.insertAdjacentText(t.position||"beforeend",this.decodeHTML(t[n].text));break;case"setAttribute":e.setAttribute(t.attrName,this.decodeHTML(t[n]));break;case"removeAttribute":e.removeAttribute(t[n]);break;case"className":e.className=this.decodeHTML(t[n]);break;case"classList.add":e.classList.add(...this.decodeHTML(t[n]).split(","));break;case"classList.remove":e.classList.remove(...this.decodeHTML(t[n]).split(","));break;case"classList.toggle":e.classList.toggle(this.decodeHTML(t[n]));break;case"classList.replace":const[s,i]=this.decodeHTML(t[n]).split(",");e.classList.replace(s,i);break;case"dataset":e.dataset[t.attrName]=this.decodeHTML(t[n]);break;case"style":Object.assign(e.style,t[n]);break;case"value":e.value=this.decodeHTML(t[n]);break;case"checked":e.checked=t[n];break;default:e.setAttribute(n,this.decodeHTML(t[n]))}}decodeHTML(e){const t=document.createElement("textarea");return t.innerHTML=e,t.value}appendAfterbegin(e){if(!e)return;const t="afterbegin-8D95D";let n=document.getElementById(t);n?(n.innerHTML=e,document.body.insertAdjacentElement("afterbegin",n)):(n=document.createElement("div"),n.id=t,n.innerHTML=e,document.body.insertAdjacentElement("afterbegin",n))}restoreSuspenseElement(e){const t=e.getAttribute("pp-original-state");if(e.hasAttribute("pp-suspense")&&t){const n=(e,t)=>{for(const n in t)t.hasOwnProperty(n)&&("textContent"===n?e.textContent=t[n]:"innerHTML"===n?e.innerHTML=t[n]:"disabled"===n?!0===t[n]?e.setAttribute("disabled","true"):e.removeAttribute("disabled"):e.setAttribute(n,t[n]));for(const n of Array.from(e.attributes))t.hasOwnProperty(n.name)||e.removeAttribute(n.name)},s=(e,t)=>{for(const s in t)if(t.hasOwnProperty(s))for(const t of Array.from(e.elements))if(t instanceof HTMLInputElement||t instanceof HTMLButtonElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement){const e=t.getAttribute("pp-original-state")||"";if(e){if(this.isJsonLike(e)){const s=this.parseJson(e);n(t,s)}else i(t,e);t.removeAttribute("pp-original-state")}}},i=(e,t)=>{e instanceof HTMLInputElement?e.value=t:e.textContent=t},a=(e,t)=>{e instanceof HTMLFormElement?s(e,t):n(e,t)};try{const i=this.parseJson(t);if(i)if(e instanceof HTMLFormElement){const t=e.id;if(t){const e=document.querySelector(`[form="${t}"]`);if(e){const t=e.getAttribute("pp-original-state");if(t&&this.isJsonLike(t)){const s=this.parseJson(t);n(e,s)}}}const i=new FormData(e),a={};if(i.forEach(((e,t)=>{a[t]=e})),s(e,a),e.hasAttribute("pp-suspense")){const t=e.getAttribute("pp-suspense")||"";if(this.parseJson(t).disabled)for(const t of Array.from(e.elements))(t instanceof HTMLInputElement||t instanceof HTMLButtonElement||t instanceof HTMLTextAreaElement||t instanceof HTMLSelectElement)&&t.removeAttribute("disabled")}}else if(i.targets){i.targets.forEach((e=>{const{id:t,...n}=e,s=document.querySelector(t);s&&a(s,n)}));const{targets:t,...s}=i;n(e,s)}else{const{empty:t,...s}=i;n(e,s)}}catch(e){}}e.querySelectorAll("[pp-suspense]").forEach((e=>this.restoreSuspenseElement(e))),e.removeAttribute("pp-original-state")}extractJson(e){const t=e?.match(/\{[\s\S]*\}/);return t?t[0]:null}getRedirectUrl(e){const t=e.match(this.redirectRegex);return t?t[1]:null}async fetchFile(e,t,n){const s=new FormData,i=n.files;if(i)for(let e=0;e<i.length;e++)s.append("file[]",i[e]);s.append("callback",t);const a=await fetch(e,{method:"POST",headers:{HTTP_PPHP_WIRE_REQUEST:"true"},body:s});return await a.text()}async 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(this.isJsonLike(e)){const n=this.parseJson(e);"disabled"!==n.onsubmit&&this.updateElementAttributes(t,n),n.targets&&n.targets.forEach((e=>{const{id:t,...n}=e,s=document.querySelector(t);s&&i(s,n)}))}else s(t,e)}},s=(e,t)=>{e instanceof HTMLInputElement?e.value=t:e.textContent=t},i=(e,t)=>{e instanceof HTMLFormElement?n(e,t):this.updateElementAttributes(e,t)};try{if(t&&this.isJsonLike(t)){const s=this.parseJson(t);if(s)if(e instanceof HTMLFormElement){const t=new FormData(e),i={};t.forEach(((e,t)=>{i[t]=e})),s.disabled&&this.toggleFormElements(e,!0);const{disabled:a,...o}=s;this.updateElementAttributes(e,o),n(e,i)}else if(s.targets){s.targets.forEach((e=>{const{id:t,...n}=e,s=document.querySelector(t);s&&i(s,n)}));const{targets:t,...n}=s;this.updateElementAttributes(e,n)}else{if("disabled"===s.empty&&""===e.value)return;const{empty:t,...n}=s;this.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){}}toggleFormElements(e,t){Array.from(e.elements).forEach((e=>{(e instanceof HTMLInputElement||e instanceof HTMLButtonElement||e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement)&&(e.disabled=t)}))}saveElementOriginalState(e){if(e.hasAttribute("pp-suspense")&&!e.hasAttribute("pp-original-state")){const t={};e.textContent&&(t.textContent=e.textContent.trim()),e.innerHTML&&(t.innerHTML=e.innerHTML.trim()),(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement)&&(t.value=e.value);for(let n=0;n<e.attributes.length;n++){const s=e.attributes[n];t[s.name]=s.value}e.setAttribute("pp-original-state",JSON.stringify(t))}if(e instanceof HTMLFormElement){let t=null;const n=e.id;n&&(t=document.querySelector(`[form="${n}"]`)),n&&t||(t=Array.from(e.elements).find((e=>e instanceof HTMLButtonElement||e instanceof HTMLInputElement))),t&&(t.hasAttribute("pp-original-state")||this.saveElementOriginalState(t))}e.querySelectorAll("[pp-suspense]").forEach((e=>this.saveElementOriginalState(e)))}getUrlParams(){const e={};return new URLSearchParams(window.location.search).forEach(((t,n)=>{e[n]=t})),e}createFetchOptions(e){return{method:"POST",headers:{"Content-Type":"application/json",HTTP_PPHP_WIRE_REQUEST:"true"},body:JSON.stringify(e)}}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=this.handleInputElement(e):(e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement)&&(n[e.name]=e.value);const i=t.match(/(\w+)\((.*)\)/);if(i){const e=i[1];let t=i[2].trim();if(t.startsWith("{")&&t.endsWith("}"))try{const e=this.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}}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}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]}}setCursorPosition(e,t){if(t.start)e.setSelectionRange(0,0);else if(t.end){const t=e.value.length||0;e.setSelectionRange(t,t)}else if(t.length){const n=parseInt(t.length,10)||0;e.setSelectionRange(n,n)}}handleInputAppendParams(e,t){const n=e.getAttribute("pp-append-params"),s=e.getAttribute("pp-append-params-sync");if("true"===n){if("true"===s){const t=e.name||e.id;if(t){const n=new URL(window.location.href),s=new URLSearchParams(n.search);s.has(t)&&(e.value=s.get(t)||"")}}e.addEventListener(t,(e=>{const t=e.currentTarget,n=t.value.trim(),s=new URL(window.location.href),i=new URLSearchParams(s.search),a=t.name||t.id;if(a){n?i.set(a,n):i.delete(a);const e=i.toString()?`${s.pathname}?${i.toString()}`:s.pathname;history.replaceState(null,"",e)}}))}}handleHiddenAttribute(){const e=document.querySelectorAll("[pp-visibility]"),t=document.querySelectorAll("[pp-display]");e.forEach((e=>this.handleVisibilityElementAttribute(e,"pp-visibility",this.handleElementVisibility))),t.forEach((e=>this.handleVisibilityElementAttribute(e,"pp-display",this.handleElementDisplay)))}handleVisibilityElementAttribute(e,t,n){const s=e.getAttribute(t);if(s)if(this.isJsonLike(s)){n(e,this.parseJson(s))}else{const n=this.parseTime(s);if(n>0){const s="pp-visibility"===t?"visibility":"display",i="visibility"===s?"hidden":"none";this.scheduleChange(e,n,s,i)}}}handleElementVisibility(e,t){this.handleElementChange(e,t,"visibility","hidden","visible")}handleElementDisplay(e,t){this.handleElementChange(e,t,"display","none","block")}handleElementChange(e,t,n,s,i){const a=t.start?this.parseTime(t.start):0,o=t.end?this.parseTime(t.end):0;a>0?(e.style[n]=s,this.scheduleChange(e,a,n,i),o>0&&this.scheduleChange(e,a+o,n,s)):o>0&&this.scheduleChange(e,o,n,s)}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(),!this.isNavigating)){this.isNavigating=!0;try{if(/^(https?:)?\/\//i.test(n)&&!n.startsWith(window.location.origin))window.location.href=n;else{const e=t.getAttribute("pp-append-params");if(n.startsWith("?")&&"true"===e){const e=new URL(window.location.href),t=new URLSearchParams(e.search);let s="";const[i,a]=n.split("#");a&&(s=`#${a}`);new URLSearchParams(i.split("?")[1]).forEach(((e,n)=>{t.set(n,e)}));const o=`${e.pathname}?${t.toString()}${s}`;history.pushState(null,"",o)}else{const[e,t]=n.split("#"),s=`${e}${t?`#${t}`:""}`;history.pushState(null,"",s)}const s=n.indexOf("#");if(-1!==s){const e=n.slice(s+1),t=document.getElementById(e);if(t)t.scrollIntoView({behavior:"smooth"});else{await this.handleNavigation();const t=document.getElementById(e);t&&t.scrollIntoView({behavior:"smooth"})}}else await this.handleNavigation()}}catch(e){}finally{this.isNavigating=!1}}}))}async handleNavigation(){try{const e=document.getElementById("loading-file-1B87E");if(e){const t=this.findLoadingElement(e,window.location.pathname);t&&await this.updateContentWithTransition(t)}const t=await this.fetch(window.location.href);if(!t.ok)return;const n=await t.text(),s=n.match(this.redirectRegex);if(s&&s[1])return void await this.handleRedirect(s[1]);await this.updateDocumentContent(n)}catch(e){}}findLoadingElement(e,t){let n=t;for(;;){const t=e.querySelector(`div[pp-loading-url='${n}']`);if(t)return t;if("/"===n)break;const s=n.lastIndexOf("/");n=s<=0?"/":n.substring(0,s)}return e.querySelector("div[pp-loading-url='/' ]")}async updateContentWithTransition(e){const t=document.querySelector("[pp-loading-content='true']")||document.body;if(!t)return;const{fadeIn:n,fadeOut:s}=this.parseTransition(e);await this.fadeOut(t,s),t.innerHTML=e.innerHTML,this.fadeIn(t,n)}parseTransition(e){let t=250,n=250;const s=e.querySelector("[pp-loading-transition]")?.getAttribute("pp-loading-transition");if(s)try{const e=this.parseJson(s);t=this.parseTime(e.fadeIn??t),n=this.parseTime(e.fadeOut??n)}catch(e){}return{fadeIn:t,fadeOut:n}}fadeOut(e,t){return new Promise((n=>{e.style.transition=`opacity ${t}ms ease-out`,e.style.opacity="0",setTimeout((()=>{e.style.transition="",n()}),t)}))}fadeIn(e,t){e.style.transition=`opacity ${t}ms ease-in`,e.style.opacity="1",setTimeout((()=>{e.style.transition=""}),t)}async updateDocumentContent(e){const t=this.saveScrollPositions(),n=(new DOMParser).parseFromString(e,"text/html"),s="pp-dynamic-script",i="pp-dynamic-link";document.head.querySelectorAll("[pp-dynamic-meta]").forEach((e=>e.remove()));document.head.querySelectorAll("[pp-dynamic-link]").forEach((e=>e.remove()));document.head.querySelectorAll("[pp-dynamic-script]").forEach((e=>e.remove()));document.removeAllEventListeners("PPBodyLoaded"),await(async e=>{Array.from(e.head.children).forEach((e=>{const t=e.tagName;if("SCRIPT"===t&&e.hasAttribute(s)){const t=document.createElement("script");Array.from(e.attributes).forEach((e=>t.setAttribute(e.name,e.value))),e.textContent&&(t.textContent=e.textContent),document.head.appendChild(t)}else if("META"===t){if(e.getAttribute("charset")||"viewport"===e.getAttribute("name"))return;const t=e.name,n=e.getAttribute("property"),s=document.head.querySelector(t?`meta[name="${t}"]`:`meta[property="${n}"]`),i=document.head.querySelector("title");s?document.head.replaceChild(e.cloneNode(!0),s):i?.nextSibling?document.head.insertBefore(e.cloneNode(!0),i.nextSibling):document.head.appendChild(e.cloneNode(!0))}else if("TITLE"===t){const t=document.head.querySelector("title");t?document.head.replaceChild(e.cloneNode(!0),t):document.head.appendChild(e.cloneNode(!0))}else if("LINK"===t){const t=t=>{const n=document.head.querySelector('link[rel="icon"]');if(n)document.head.replaceChild(e.cloneNode(!0),n);else{const e=document.createElement("link");e.rel="icon",e.href=t,document.head.appendChild(e)}};if("icon"===e.getAttribute("rel")){t(e.href)}else if(e.hasAttribute(i)){const t=e.cloneNode(!0);document.head.appendChild(t)}}})),await this.populateDocumentBody(e)})(n),this.restoreScrollPositions(t),this.attachWireFunctionEvents(),document.dispatchEvent(new Event("PPBodyLoaded"))}restoreScrollPositions(e){requestAnimationFrame((()=>{const t=e.window;t&&window.scrollTo(t.scrollLeft,t.scrollTop),document.querySelectorAll("*").forEach((t=>{const n=this.getElementKey(t);e[n]&&(t.scrollTop=e[n].scrollTop,t.scrollLeft=e[n].scrollLeft)}))}))}async populateDocumentBody(e){try{const t=e.body.cloneNode(!0);this.manageScriptTags(t),document.body.replaceWith(t)}catch(e){}}manageScriptTags(e,t){const n=e.querySelectorAll("script"),s=t?.querySelectorAll("script")||n;n.forEach(((e,t)=>{const n=document.createElement("script"),i=s[t]||e;Array.from(i.attributes).forEach((e=>{n.setAttribute(e.name,e.value)})),i.hasAttribute("src")||(n.textContent=i.textContent),e.parentNode?.replaceChild(n,e)}))}saveScrollPositions(){const e={window:{scrollTop:window.scrollY||document.documentElement.scrollTop,scrollLeft:window.scrollX||document.documentElement.scrollLeft}};return document.querySelectorAll("*").forEach((t=>{(t.scrollTop||t.scrollLeft)&&(e[this.getElementKey(t)]={scrollTop:t.scrollTop,scrollLeft:t.scrollLeft})})),e}getElementKey(e){return e.id||e.className||e.tagName}async handleRedirect(e){if(e)try{const t=new URL(e,window.location.origin);t.origin!==window.location.origin?window.location.href=e:(history.pushState(null,"",e),await this.handleNavigation())}catch(e){}}async fetch(e,t){return fetch(e,{...t,headers:{...t?.headers,"X-Requested-With":"XMLHttpRequest"}})}isJsonLike(e){return"string"==typeof e&&((e=e.trim()).startsWith("{")&&e.endsWith("}")||e.startsWith("[")&&e.endsWith("]"))}parseJson(e){try{return JSON5.parse(e)}catch(e){return null}}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}scheduleChange(e,t,n,s){setTimeout((()=>{requestAnimationFrame((()=>{e.style[n]=s}))}),t)}observeDOMChanges(){new MutationObserver((e=>{for(const t of e)"childList"===t.type&&t.addedNodes.length>0&&this.attachWireFunctionEvents()})).observe(document.body,{childList:!0,subtree:!0})}async fetchFunction(e,t={}){try{const n={callback:e,...t},s=this.createFetchOptions(n),i=await this.fetch(window.location.href,s);if(!i.ok)throw new Error(`Fetch failed with status: ${i.status} ${i.statusText}`);const a=await i.text();try{return JSON.parse(a)}catch{return a}}catch(e){throw new Error("Failed to fetch data.")}}async sync(...e){try{const t=e.length>0?e.map((e=>`pp-sync="${e}"`)):['pp-sync="true"'],n=this.createFetchOptions({secondRequestC69CD:!0,...this.getUrlParams()}),s=await this.fetch(window.location.href,n),i=await s.text(),a=(new DOMParser).parseFromString(i,"text/html");t.forEach((e=>{const t=document.querySelectorAll(`[${e}]`),n=a.body.querySelectorAll(`[${e}]`);t.forEach(((e,t)=>{const s=n[t];s&&(e.innerHTML=s.innerHTML,this.reRunScripts(e))}))}))}catch(e){}}async fetchAndUpdateBodyContent(){const e=this.createFetchOptions({secondRequestC69CD:!0,...this.getUrlParams()}),t=await this.fetch(window.location.href,e),n=await t.text();await this.updateBodyContent(n)}reRunScripts(e){e.querySelectorAll("script").forEach((e=>{const t=document.createElement("script");Array.from(e.attributes).forEach((e=>{t.setAttribute(e.name,e.value)})),e.hasAttribute("src")||(t.textContent=e.textContent),e.parentNode?.replaceChild(t,e)}))}copyCode(e,t,n,s,i="img",a=2e3){if(!(e instanceof HTMLElement))return;const o=e.closest(`.${t}`)?.querySelector("pre code"),r=o?.textContent?.trim()||"";r?navigator.clipboard.writeText(r).then((()=>{const t=e.querySelector(i);if(t)for(const[e,n]of Object.entries(s))e in t?t[e]=n:t.setAttribute(e,n);setTimeout((()=>{if(t)for(const[e,s]of Object.entries(n))e in t?t[e]=s:t.setAttribute(e,s)}),a)}),(()=>{alert("Failed to copy command to clipboard")})):alert("Failed to find the code block to copy")}getCookie(e){return document.cookie.split("; ").find((t=>t.startsWith(e+"=")))?.split("=")[1]||null}}class PPHPLocalStore{static instance=null;state;listeners;pphp;STORAGE_KEY;constructor(e={}){this.state=e,this.listeners=[],this.pphp=PPHP.instance,this.STORAGE_KEY=this.pphp.getCookie("pphp_local_store_key")||"pphp_local_store_59e13",this.loadState()}static getInstance(e={}){return PPHPLocalStore.instance||(PPHPLocalStore.instance=new PPHPLocalStore(e)),PPHPLocalStore.instance}setState(e,t=!1){if(this.state={...this.state,...e},this.listeners.forEach((e=>e(this.state))),this.saveState(),t){const e=localStorage.getItem(this.STORAGE_KEY);e&&this.pphp.fetchFunction(this.STORAGE_KEY,{[this.STORAGE_KEY]:e})}}saveState(){localStorage.setItem(this.STORAGE_KEY,JSON.stringify(this.state))}loadState(){const e=localStorage.getItem(this.STORAGE_KEY);e&&(this.state=this.pphp.parseJson(e),this.listeners.forEach((e=>e(this.state))))}resetState(e,t=!1){if(e?(delete this.state[e],this.saveState()):(this.state={},localStorage.removeItem(this.STORAGE_KEY)),this.listeners.forEach((e=>e(this.state))),t){const t=e?localStorage.getItem(this.STORAGE_KEY):null;this.pphp.fetchFunction(this.STORAGE_KEY,{[this.STORAGE_KEY]:t})}}}
|
|
2
2
|
// Initialize the PPHP instance
|
|
3
3
|
document.addEventListener("PPBodyLoaded", () => {
|
|
4
4
|
PPHP.instance.attachWireFunctionEvents();
|
|
@@ -9,4 +9,4 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
9
9
|
document.dispatchEvent(new Event("PPBodyLoaded"));
|
|
10
10
|
});
|
|
11
11
|
var pphp = PPHP.instance;
|
|
12
|
-
var store = PPHPLocalStore.getInstance();
|
|
12
|
+
var store = PPHPLocalStore.getInstance();
|