diff --git a/.github/workflows/copyright.yml b/.github/workflows/copyright.yml index 5b27e21e1..884666ab4 100644 --- a/.github/workflows/copyright.yml +++ b/.github/workflows/copyright.yml @@ -53,7 +53,7 @@ jobs: # Only check files with specific extensions extension="${file##*.}" - if ! [[ "${extension,,}" =~ ^(css|js|php|phtml|template|xml)$ ]]; then + if ! [[ "${extension,,}" =~ ^(css|js|php|phtml)$ ]]; then continue fi diff --git a/app/code/core/Maho/Captcha/Helper/Data.php b/app/code/core/Maho/Captcha/Helper/Data.php new file mode 100644 index 000000000..1ccd767b8 --- /dev/null +++ b/app/code/core/Maho/Captcha/Helper/Data.php @@ -0,0 +1,129 @@ + */ + protected static array $_payloadVerificationCache = []; + + public function isEnabled(): bool + { + return $this->isModuleEnabled() && $this->isModuleOutputEnabled() && Mage::getStoreConfigFlag(self::XML_PATH_ENABLED); + } + + public function getTableName(): string + { + return Mage::getSingleton('core/resource')->getTableName('captcha/challenge'); + } + + public function getHmacKey(): string + { + return (string) Mage::getConfig()->getNode('global/crypt/key'); + } + + public function getFrontendSelectors(): string + { + $selectors = Mage::getStoreConfig(self::XML_PATH_FRONTEND_SELECTORS) ?? ''; + $selectors = trim($selectors); + $selectors = str_replace(["\r\n", "\r"], "\n", $selectors); + $selectors = explode("\n", $selectors); + + $selectorsToKeep = []; + foreach ($selectors as $selector) { + $selector = trim($selector); + if (strlen($selector) && !str_starts_with($selector, '//')) { + $selectorsToKeep[] = $selector; + } + } + + return implode(',', $selectorsToKeep); + } + + public function getChallengeUrl(): string + { + return Mage::getUrl('captcha/index/challenge'); + } + + public function getWidgetAttributes(): Varien_Object + { + return new Varien_Object([ + 'challengeurl' => $this->getChallengeUrl(), + 'name' => 'maho_captcha', + 'auto' => 'onload', + 'hidelogo' => '', + 'hidefooter' => '', + 'refetchonexpire' => '', + ]); + } + + public function createChallenge(?array $options = null): Altcha\Challenge + { + $options = new Altcha\ChallengeOptions([ + 'algorithm' => Altcha\Algorithm::SHA512, + 'saltLength' => 32, + 'expires' => (new DateTime())->modify('+1 minute'), + 'hmacKey' => $this->getHmacKey(), + ...($options ?? []), + ]); + return Altcha\Altcha::createChallenge($options); + } + + public function verify(string $payload): bool + { + if (empty($payload)) { + return false; + } + + if (isset(self::$_payloadVerificationCache[$payload])) { + return self::$_payloadVerificationCache[$payload]; + } + + // Check that the challenge is not stored in the database, meaning it was already solved + $coreRead = Mage::getSingleton('core/resource')->getConnection('core_read'); + $select = $coreRead->select() + ->from($this->getTableName(), ['challenge']) + ->where('challenge = ?', $payload); + if ($coreRead->fetchOne($select)) { + return false; + } + + try { + $isValid = Altcha\Altcha::verifySolution($payload, $this->getHmacKey(), true); + $this->logChallenge($payload); + } catch (Exception $e) { + $isValid = false; + Mage::logException($e); + } + + self::$_payloadVerificationCache[$payload] = $isValid; + return $isValid; + } + + protected function logChallenge(string $payload): void + { + try { + Mage::getSingleton('core/resource') + ->getConnection('core_write') + ->insert($this->getTableName(), [ + 'challenge' => $payload, + 'created_at' => Mage::getModel('core/date')->gmtDate('Y-m-d H:i:s'), + ]); + } catch (Exception $e) { + Mage::logException($e); + } + } +} diff --git a/app/code/core/Maho/Captcha/Model/Challenge.php b/app/code/core/Maho/Captcha/Model/Challenge.php new file mode 100644 index 000000000..d061bc5f1 --- /dev/null +++ b/app/code/core/Maho/Captcha/Model/Challenge.php @@ -0,0 +1,17 @@ +_init('captcha/challenge'); + } +} diff --git a/app/code/core/Maho/Captcha/Model/Observer.php b/app/code/core/Maho/Captcha/Model/Observer.php new file mode 100644 index 000000000..0be054ec9 --- /dev/null +++ b/app/code/core/Maho/Captcha/Model/Observer.php @@ -0,0 +1,95 @@ +isEnabled()) { + return; + } + + /** @var Mage_Core_Controller_Front_Action $controller */ + $controller = $observer->getControllerAction(); + if ($controller->getFullActionName() === 'checkout_onepage_saveBilling' && + Mage::getSingleton('customer/session')->isLoggedIn()) { + return; + } + + $data = $controller->getRequest()->getPost(); + $token = $data['maho_captcha'] ?? ''; + if ($helper->verify((string) $token)) { + return; + } + + $isAjax = (bool) ($observer->getEvent()->args['is_ajax'] ?? null); + $this->failedVerification($controller, $isAjax); + } + + public function verifyAdmin(Varien_Event_Observer $observer): void + { + $helper = Mage::helper('captcha'); + if (!$helper->isEnabled()) { + return; + } + + $request = Mage::app()->getRequest(); + if ($request->getActionName() == 'prelogin' || !$request->isPost()) { + return; + } + + $data = $request->getPost(); + $token = $data['maho_captcha'] ?? ''; + + if ($helper->verify((string) $token)) { + return; + } + + Mage::throwException(Mage::helper('captcha')->__('Incorrect CAPTCHA.')); + } + + protected function failedVerification(Mage_Core_Controller_Front_Action $controller, bool $isAjax = false): void + { + $controller->setFlag('', Mage_Core_Controller_Varien_Action::FLAG_NO_DISPATCH, true); + $errorMessage = Mage::helper('captcha')->__('Incorrect CAPTCHA.'); + + if ($isAjax) { + $result = ['error' => true, 'message' => $errorMessage]; + $controller->getResponse()->setBodyJson($result); + return; + } + + Mage::getSingleton('core/session')->addError($errorMessage); + $request = $controller->getRequest(); + $refererUrl = $request->getServer('HTTP_REFERER'); + if ($url = $request->getParam(Mage_Core_Controller_Varien_Action::PARAM_NAME_REFERER_URL)) { + $refererUrl = $url; + } elseif ($url = $request->getParam(Mage_Core_Controller_Varien_Action::PARAM_NAME_BASE64_URL)) { + $refererUrl = Mage::helper('core')->urlDecodeAndEscape($url); + } elseif ($url = $request->getParam(Mage_Core_Controller_Varien_Action::PARAM_NAME_URL_ENCODED)) { + $refererUrl = Mage::helper('core')->urlDecodeAndEscape($url); + } + $controller->getResponse()->setRedirect($refererUrl); + } + + public function cleanup() + { + $resource = Mage::getSingleton('core/resource'); + $connection = $resource->getConnection('core_write'); + $table = $resource->getTableName('captcha/challenge'); + + $cutOffDate = Mage::getModel('core/date')->gmtDate('Y-m-d H:i:s', strtotime('-1 day')); + $connection->delete( + $table, + ['created_at < ?' => $cutOffDate], + ); + } +} diff --git a/app/code/core/Maho/Captcha/Model/Resource/Challenge.php b/app/code/core/Maho/Captcha/Model/Resource/Challenge.php new file mode 100644 index 000000000..972040fa2 --- /dev/null +++ b/app/code/core/Maho/Captcha/Model/Resource/Challenge.php @@ -0,0 +1,17 @@ +_init('captcha/challenge', 'challenge'); + } +} diff --git a/app/code/core/Maho/Captcha/controllers/IndexController.php b/app/code/core/Maho/Captcha/controllers/IndexController.php new file mode 100644 index 000000000..98261010c --- /dev/null +++ b/app/code/core/Maho/Captcha/controllers/IndexController.php @@ -0,0 +1,32 @@ +isEnabled()) { + Mage::throwException($helper->__('Captcha is disabled')); + } + $this->getResponse()->setBodyJson($helper->createChallenge()); + } catch (Mage_Core_Exception $e) { + $error = $e->getMessage(); + } catch (Exception $e) { + $error = $helper->__('Internal Error'); + } + if (isset($error)) { + $this->getResponse() + ->setHttpResponseCode(400) + ->setBodyJson(['error' => true, 'message' => $error]); + } + } +} diff --git a/app/code/core/Maho/Captcha/etc/config.xml b/app/code/core/Maho/Captcha/etc/config.xml new file mode 100644 index 000000000..6d299aa4c --- /dev/null +++ b/app/code/core/Maho/Captcha/etc/config.xml @@ -0,0 +1,182 @@ + + + + + 2.0.0 + + + + + + Maho_Captcha_Model + captcha_resource + + + Maho_Captcha_Model_Resource + + + captha_challenge
+
+
+
+
+ + + Maho_Captcha_Helper + + + + + + Maho_Captcha + Mage_Core_Model_Resource_Setup + + + +
+ + + + standard + + Maho_Captcha + captcha + + + + + + + + captcha/observer + verify + 1 + + + + + + + captcha/observer + verify + + + + + + + captcha/observer + verify + + + + + + + captcha/observer + verify + + + + + + + captcha/observer + verify + + + + + + + captcha/observer + verify + + + + + + + captcha/observer + verify + + + + + + + + captcha.xml + + + + + + + + Maho_Captcha.csv + + + + + + + + + + + captcha/observer + verifyAdmin + + + + + + + captcha/observer + verifyAdmin + + + + + + + + captcha.xml + + + + + + + + Maho_Captcha.csv + + + + + + + + + 0 3 * * * + captcha/observer::cleanup + + + + + + + 1 + .checkout-onepage-index #billing-new-address-form +.contacts-index-index #contactForm +.customer-account-create #form-validate +.customer-account-forgotpassword #form-validate +#newsletter-validate-detail +.catalog-product-view #review-form +.review-product-list #review-form +.wishlist-index-share #form-validate + + + +
diff --git a/app/code/core/Maho/Captcha/etc/system.xml b/app/code/core/Maho/Captcha/etc/system.xml new file mode 100644 index 000000000..d6fab0a7a --- /dev/null +++ b/app/code/core/Maho/Captcha/etc/system.xml @@ -0,0 +1,36 @@ + + + + + + + + text + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 10 + 1 + 1 + 1 + + + + One selector per line.
To disable a selector, type // at the beginning of the line.]]>
+ textarea + 40 + 1 + 1 + 1 +
+
+
+
+
+
+
\ No newline at end of file diff --git a/app/code/core/Maho/Captcha/sql/captcha_setup/install-2.0.0.php b/app/code/core/Maho/Captcha/sql/captcha_setup/install-2.0.0.php new file mode 100644 index 000000000..42adf89dd --- /dev/null +++ b/app/code/core/Maho/Captcha/sql/captcha_setup/install-2.0.0.php @@ -0,0 +1,26 @@ +startSetup(); + +$table = $installer->getConnection() + ->newTable($installer->getTable('captcha/challenge')) + ->addColumn('challenge', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, [ + 'nullable' => false, + 'primary' => true, + ]) + ->addColumn('created_at', Varien_Db_Ddl_Table::TYPE_DATETIME, null, [ + 'nullable' => false, + ]); +$installer->getConnection()->createTable($table); + +$installer->endSetup(); diff --git a/app/code/core/Maho/Captcha/sql/captcha_setup/upgrade-1.7.0.0.0-2.0.0.php b/app/code/core/Maho/Captcha/sql/captcha_setup/upgrade-1.7.0.0.0-2.0.0.php new file mode 100644 index 000000000..39bf3edb0 --- /dev/null +++ b/app/code/core/Maho/Captcha/sql/captcha_setup/upgrade-1.7.0.0.0-2.0.0.php @@ -0,0 +1,11 @@ + + + + + + + + + + + + + diff --git a/app/design/adminhtml/default/default/template/captcha/captcha.phtml b/app/design/adminhtml/default/default/template/captcha/captcha.phtml new file mode 100644 index 000000000..6a398dd66 --- /dev/null +++ b/app/design/adminhtml/default/default/template/captcha/captcha.phtml @@ -0,0 +1,23 @@ +isEnabled()) return ''; +?> +getWidgetAttributes()->serialize() ?>> + + diff --git a/app/design/frontend/base/default/layout/captcha.xml b/app/design/frontend/base/default/layout/captcha.xml new file mode 100644 index 000000000..6aaf9c1dd --- /dev/null +++ b/app/design/frontend/base/default/layout/captcha.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/design/frontend/base/default/template/captcha/footer.phtml b/app/design/frontend/base/default/template/captcha/footer.phtml new file mode 100644 index 000000000..99768e5d8 --- /dev/null +++ b/app/design/frontend/base/default/template/captcha/footer.phtml @@ -0,0 +1,23 @@ +isEnabled()) return ''; +?> +getWidgetAttributes()->serialize() ?>> + + diff --git a/app/design/frontend/base/default/template/checkout/onepage/billing.phtml b/app/design/frontend/base/default/template/checkout/onepage/billing.phtml index 648743faa..aea027594 100644 --- a/app/design/frontend/base/default/template/checkout/onepage/billing.phtml +++ b/app/design/frontend/base/default/template/checkout/onepage/billing.phtml @@ -188,7 +188,7 @@
- + diff --git a/app/etc/modules/Maho_Captcha.xml b/app/etc/modules/Maho_Captcha.xml new file mode 100644 index 000000000..d52db3460 --- /dev/null +++ b/app/etc/modules/Maho_Captcha.xml @@ -0,0 +1,9 @@ + + + + + true + core + + + \ No newline at end of file diff --git a/app/locale/en_US/Maho_Captcha.csv b/app/locale/en_US/Maho_Captcha.csv new file mode 100644 index 000000000..ff519c3af --- /dev/null +++ b/app/locale/en_US/Maho_Captcha.csv @@ -0,0 +1,6 @@ +"Captcha is disabled","Captcha is disabled" +"CSS Selectors","CSS Selectors" +"Enable","Enable" +"Incorrect CAPTCHA.","Incorrect CAPTCHA." +"Internal Error","Internal Error" +"These CSS selectors are used to identify the forms for which Turnstile will be activated.
One selector per line.
To disable a selector, type // at the beginning of the line.","These CSS selectors are used to identify the forms for which Turnstill will be activated.
One selector per line.
To disable a selector, type // at the beginning of the line." diff --git a/composer.json b/composer.json index f5463c0cf..ea4ce494b 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,7 @@ "bacon/bacon-qr-code": "^3.0", "symfony/polyfill-php83": "^1.31", "lbuchs/webauthn": "^2.2", + "altcha-org/altcha": "^0.1.3", "mahocommerce/icons": "^1", "symfony/mailer": "^7.2", "symfony/polyfill-php84": "^1.31" diff --git a/composer.lock b/composer.lock index 600f63368..77d87dd02 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,50 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1ca469952c5c632830e65da4c6e88ef2", + "content-hash": "728d5d729455cf3e013ff18e3c079eb3", "packages": [ + { + "name": "altcha-org/altcha", + "version": "v0.1.4", + "source": { + "type": "git", + "url": "https://github.com/altcha-org/altcha-lib-php.git", + "reference": "0371b6bd6ac1e7e4b78e47a884e5fa3d062720e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/altcha-org/altcha-lib-php/zipball/0371b6bd6ac1e7e4b78e47a884e5fa3d062720e6", + "reference": "0371b6bd6ac1e7e4b78e47a884e5fa3d062720e6", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "AltchaOrg\\Altcha\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Regeci", + "email": "536331+ovx@users.noreply.github.com" + } + ], + "support": { + "issues": "https://github.com/altcha-org/altcha-lib-php/issues", + "source": "https://github.com/altcha-org/altcha-lib-php/tree/v0.1.4" + }, + "time": "2025-02-28T05:17:34+00:00" + }, { "name": "bacon/bacon-qr-code", "version": "v3.0.1", diff --git a/public/js/altcha.min.js b/public/js/altcha.min.js new file mode 100644 index 000000000..e53ce6372 --- /dev/null +++ b/public/js/altcha.min.js @@ -0,0 +1,8 @@ +/** + * Minified by jsDelivr using Terser v5.37.0. + * Original file: /npm/altcha@1.2.0/dist/altcha.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +var hi=Object.defineProperty,Pr=e=>{throw TypeError(e)},gi=(e,t,n)=>t in e?hi(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,re=(e,t,n)=>gi(e,"symbol"!=typeof t?t+"":t,n),zr=(e,t,n)=>t.has(e)||Pr("Cannot "+n),M=(e,t,n)=>(zr(e,t,"read from private field"),n?n.call(e):t.get(e)),Yt=(e,t,n)=>t.has(e)?Pr("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),Ft=(e,t,n,r)=>(zr(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n);const Hr="KGZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2NvbnN0IGQ9bmV3IFRleHRFbmNvZGVyO2Z1bmN0aW9uIHAoZSl7cmV0dXJuWy4uLm5ldyBVaW50OEFycmF5KGUpXS5tYXAodD0+dC50b1N0cmluZygxNikucGFkU3RhcnQoMiwiMCIpKS5qb2luKCIiKX1hc3luYyBmdW5jdGlvbiBiKGUsdCxyKXtpZih0eXBlb2YgY3J5cHRvPiJ1Inx8ISgic3VidGxlImluIGNyeXB0byl8fCEoImRpZ2VzdCJpbiBjcnlwdG8uc3VidGxlKSl0aHJvdyBuZXcgRXJyb3IoIldlYiBDcnlwdG8gaXMgbm90IGF2YWlsYWJsZS4gU2VjdXJlIGNvbnRleHQgaXMgcmVxdWlyZWQgKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL1NlY3VyaXR5L1NlY3VyZV9Db250ZXh0cykuIik7cmV0dXJuIHAoYXdhaXQgY3J5cHRvLnN1YnRsZS5kaWdlc3Qoci50b1VwcGVyQ2FzZSgpLGQuZW5jb2RlKGUrdCkpKX1mdW5jdGlvbiB3KGUsdCxyPSJTSEEtMjU2IixuPTFlNixzPTApe2NvbnN0IG89bmV3IEFib3J0Q29udHJvbGxlcixhPURhdGUubm93KCk7cmV0dXJue3Byb21pc2U6KGFzeW5jKCk9Pntmb3IobGV0IGM9cztjPD1uO2MrPTEpe2lmKG8uc2lnbmFsLmFib3J0ZWQpcmV0dXJuIG51bGw7aWYoYXdhaXQgYih0LGMscik9PT1lKXJldHVybntudW1iZXI6Yyx0b29rOkRhdGUubm93KCktYX19cmV0dXJuIG51bGx9KSgpLGNvbnRyb2xsZXI6b319ZnVuY3Rpb24gaChlKXtjb25zdCB0PWF0b2IoZSkscj1uZXcgVWludDhBcnJheSh0Lmxlbmd0aCk7Zm9yKGxldCBuPTA7bjx0Lmxlbmd0aDtuKyspcltuXT10LmNoYXJDb2RlQXQobik7cmV0dXJuIHJ9ZnVuY3Rpb24gZyhlLHQ9MTIpe2NvbnN0IHI9bmV3IFVpbnQ4QXJyYXkodCk7Zm9yKGxldCBuPTA7bjx0O24rKylyW25dPWUlMjU2LGU9TWF0aC5mbG9vcihlLzI1Nik7cmV0dXJuIHJ9YXN5bmMgZnVuY3Rpb24gbShlLHQ9IiIscj0xZTYsbj0wKXtjb25zdCBzPSJBRVMtR0NNIixvPW5ldyBBYm9ydENvbnRyb2xsZXIsYT1EYXRlLm5vdygpLGw9YXN5bmMoKT0+e2ZvcihsZXQgdT1uO3U8PXI7dSs9MSl7aWYoby5zaWduYWwuYWJvcnRlZHx8IWN8fCF5KXJldHVybiBudWxsO3RyeXtjb25zdCBmPWF3YWl0IGNyeXB0by5zdWJ0bGUuZGVjcnlwdCh7bmFtZTpzLGl2OmcodSl9LGMseSk7aWYoZilyZXR1cm57Y2xlYXJUZXh0Om5ldyBUZXh0RGVjb2RlcigpLmRlY29kZShmKSx0b29rOkRhdGUubm93KCktYX19Y2F0Y2h7fX1yZXR1cm4gbnVsbH07bGV0IGM9bnVsbCx5PW51bGw7dHJ5e3k9aChlKTtjb25zdCB1PWF3YWl0IGNyeXB0by5zdWJ0bGUuZGlnZXN0KCJTSEEtMjU2IixkLmVuY29kZSh0KSk7Yz1hd2FpdCBjcnlwdG8uc3VidGxlLmltcG9ydEtleSgicmF3Iix1LHMsITEsWyJkZWNyeXB0Il0pfWNhdGNoe3JldHVybntwcm9taXNlOlByb21pc2UucmVqZWN0KCksY29udHJvbGxlcjpvfX1yZXR1cm57cHJvbWlzZTpsKCksY29udHJvbGxlcjpvfX1sZXQgaTtvbm1lc3NhZ2U9YXN5bmMgZT0+e2NvbnN0e3R5cGU6dCxwYXlsb2FkOnIsc3RhcnQ6bixtYXg6c309ZS5kYXRhO2xldCBvPW51bGw7aWYodD09PSJhYm9ydCIpaT09bnVsbHx8aS5hYm9ydCgpLGk9dm9pZCAwO2Vsc2UgaWYodD09PSJ3b3JrIil7aWYoIm9iZnVzY2F0ZWQiaW4gcil7Y29uc3R7a2V5OmEsb2JmdXNjYXRlZDpsfT1yfHx7fTtvPWF3YWl0IG0obCxhLHMsbil9ZWxzZXtjb25zdHthbGdvcml0aG06YSxjaGFsbGVuZ2U6bCxzYWx0OmN9PXJ8fHt9O289dyhsLGMsYSxzLG4pfWk9by5jb250cm9sbGVyLG8ucHJvbWlzZS50aGVuKGE9PntzZWxmLnBvc3RNZXNzYWdlKGEmJnsuLi5hLHdvcmtlcjohMH0pfSl9fX0pKCk7Cg==",_i=e=>Uint8Array.from(atob(e),(e=>e.charCodeAt(0))),Gr=typeof self<"u"&&self.Blob&&new Blob([_i(Hr)],{type:"text/javascript;charset=utf-8"});function mi(e){let t;try{if(t=Gr&&(self.URL||self.webkitURL).createObjectURL(Gr),!t)throw"";const n=new Worker(t,{name:null==e?void 0:e.name});return n.addEventListener("error",(()=>{(self.URL||self.webkitURL).revokeObjectURL(t)})),n}catch{return new Worker("data:text/javascript;base64,"+Hr,{name:null==e?void 0:e.name})}finally{t&&(self.URL||self.webkitURL).revokeObjectURL(t)}}const bi="5";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(bi);const yi=1,pi=4,wi=8,Ei=16,xi=1,ki=2,Jr="[",Kr="[!",qr="]",Ue={},j=Symbol(),Qr=!1;var en=Array.isArray,Ci=Array.from,xt=Object.keys,kt=Object.defineProperty,Ne=Object.getOwnPropertyDescriptor,Ri=Object.getOwnPropertyDescriptors,Ii=Object.prototype,$i=Array.prototype,Xt=Object.getPrototypeOf;function tn(e){for(var t=0;t=h.v&&T(h,p+1)}Dr(a)}return!0},ownKeys(e){v(a);var t=Reflect.ownKeys(e).filter((e=>{var t=l.get(e);return void 0===t||t.v!==j}));for(var[n,r]of l)r.v!==j&&!(n in e)&&t.push(n);return t},setPrototypeOf(){Fi()}})}function Dr(e,t=1){T(e,e.v+t)}var Yr,cn,dn;function Wt(){if(void 0===Yr){Yr=window;var e=Element.prototype,t=Node.prototype;cn=Ne(t,"firstChild").get,dn=Ne(t,"nextSibling").get,e.__click=void 0,e.__className="",e.__attributes=null,e.__styles=null,e.__e=void 0,Text.prototype.__t=void 0}}function Nt(e=""){return document.createTextNode(e)}function Le(e){return cn.call(e)}function Re(e){return dn.call(e)}function J(e,t){if(!P)return Le(e);var n=Le(V);return null===n&&(n=V.appendChild(Nt())),ke(n),n}function Ot(e,t){if(!P){var n=Le(e);return n instanceof Comment&&""===n.data?Re(n):n}return V}function ge(e,t=1,n=!1){let r=P?V:e;for(var o;t--;)o=r,r=Re(r);if(!P)return r;var l=null==r?void 0:r.nodeType;if(n&&3!==l){var i=Nt();return null===r?null==o||o.after(i):r.before(i),ke(i),i}return ke(r),r}function Mi(e){e.textContent=""}function ot(e){var t=2050;null===y?t|=Ae:y.f|=nn;var n=null!==R&&2&R.f?R:null;const r={children:null,ctx:X,deps:null,equals:an,f:t,fn:e,reactions:null,v:null,version:0,parent:n??y};return null!==n&&(n.children??(n.children=[])).push(r),r}function vn(e){var t=e.children;if(null!==t){e.children=null;for(var n=0;n{Te(t)}}function Qt(e){return qe(4,e,!1)}function er(e){return qe(8,e,!0)}function Fe(e){return tr(e)}function tr(e,t=0){return qe(24|t,e,!0)}function Rt(e,t=!0){return qe(40,e,!0,t)}function mn(e){var t=e.teardown;if(null!==t){const e=nr,n=R;Zr(!0),Ce(null);try{t.call(null)}finally{Zr(e),Ce(n)}}}function bn(e){var t=e.deriveds;if(null!==t){e.deriveds=null;for(var n=0;n{Te(e),t&&t()}))}function qi(e,t){var n=e.length;if(n>0){var r=()=>--n||t();for(var o of e)o.out(r)}else t()}function wn(e,t,n){if(!(e.f&Be)){if(e.f^=Be,null!==e.transitions)for(const r of e.transitions)(r.is_global||n)&&t.push(r);for(var r=e.first;null!==r;){var o=r.next;wn(r,t,!!(!!(r.f&Kt)||!!(32&r.f))&&n),r=o}}}function Or(e){En(e,!0)}function En(e,t){if(e.f&Be){_t(e)&&At(e),e.f^=Be;for(var n=e.first;null!==n;){var r=n.next;En(n,!!(!!(n.f&Kt)||!!(32&n.f))&&t),n=r}if(null!==e.transitions)for(const n of e.transitions)(n.is_global||t)&&n.in()}}const Qi=typeof requestIdleCallback>"u"?e=>setTimeout(e,1):requestIdleCallback;let It=!1,$t=!1,Mt=[],jt=[];function xn(){It=!1;const e=Mt.slice();Mt=[],tn(e)}function kn(){$t=!1;const e=jt.slice();jt=[],tn(e)}function rr(e){It||(It=!0,queueMicrotask(xn)),Mt.push(e)}function el(e){$t||($t=!0,Qi(kn)),jt.push(e)}function tl(){It&&xn(),$t&&kn()}function Cn(e){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}const Rn=0,rl=1;let pt=!1,wt=Rn,ft=!1,ct=null,Me=!1,nr=!1;function Xr(e){Me=e}function Zr(e){nr=e}let Se=[],je=0,R=null;function Ce(e){R=e}let y=null;function de(e){y=e}let ce=null;function nl(e){ce=e}let O=null,q=0,we=null;function il(e){we=e}let In=0,We=!1,X=null;function $n(){return++In}function ll(){return!sn}function _t(e){var t,n,r=e.f;if(r&xe)return!0;if(r>){var o=e.deps,l=!!(r&Ae);if(null!==o){var i;if(r&Ct){for(i=0;ie.version)return!0}}l||ve(e,B)}return!1}function al(e,t){for(var n=t;null!==n;){if(n.f&Zt)try{return void n.fn(e)}catch{n.f^=Zt}n=n.parent}throw pt=!1,e}function ol(e){return!(e.f&Ke||null!==e.parent&&e.parent.f&Zt)}function Lt(e,t,n,r){if(pt){if(null===n&&(pt=!1),ol(t))throw e}else null!==n&&(pt=!0),al(e,t)}function Sn(e){var t,n=O,r=q,o=we,l=R,i=We,a=ce,s=X,c=e.f;O=null,q=0,we=null,R=96&c?null:e,We=!Me&&!!(c&Ae),ce=null,X=e.ctx;try{var u=(0,e.fn)(),d=e.deps;if(null!==O){var f;if(dt(e,q),null!==d&&q>0)for(d.length=q+O.length,f=0;f1e3){je=0;try{zi()}catch(e){if(null===ct)throw e;Lt(e,ct,null)}}je++}function Ln(e){var t=e.length;if(0!==t){Nn();var n=Me;Me=!0;try{for(var r=0;r1001)return;const e=Se;Se=[],Ln(e),ft||(je=0,ct=null)}function Tt(e){wt===Rn&&(ft||(ft=!0,queueMicrotask(fl))),ct=e;for(var t=e;null!==t.parent;){var n=(t=t.parent).f;if(96&n){if(!(n&B))return;t.f^=B}}Se.push(t)}function An(e,t){var n=e.first,r=[];e:for(;null!==n;){var o=n.f,l=!!(32&o),i=l&&!!(o&B),a=n.next;if(!(i||o&Be))if(8&o){if(l)n.f^=B;else try{_t(n)&&At(n)}catch(e){Lt(e,n,null,n.ctx)}var s=n.first;if(null!==s){n=s;continue}}else 4&o&&r.push(n);if(null===a){let t=n.parent;for(;null!==t;){if(e===t)break e;var c=t.next;if(null!==c){n=c;continue e}t=t.parent}}n=a}for(var u=0;u0||t.length>0)&&k(),je=0,ct=null,r}finally{wt=t,Se=n}}async function cl(){await Promise.resolve(),k()}function v(e){var t,n=e.f,r=!!(2&n);if(r&&n&Ke){var o=hn(e);return qt(e),o}if(null!==R){null!==ce&&ce.includes(e)&&Oi();var l=R.deps;null===O&&null!==l&&l[q]===e?q++:null===O?O=[e]:O.push(e),null!==we&&null!==y&&y.f&B&&!(32&y.f)&&we.includes(e)&&(ve(y,xe),Tt(y))}else if(r&&null===e.deps)for(var i=e,a=i.parent,s=i;null!==a;){if(!(2&a.f)){var c=a;null!=(t=c.deriveds)&&t.includes(s)||(c.deriveds??(c.deriveds=[])).push(s);break}s=a,a=a.parent}return r&&(_t(i=e)&&gn(i)),e.v}function Je(e){const t=R;try{return R=null,e()}finally{R=t}}const dl=-7169;function ve(e,t){e.f=e.f&dl|t}function Tn(e,t=!1,n){X={p:X,c:null,e:null,m:!1,s:e,x:null,l:null}}function Vn(e){const t=X;if(null!==t){void 0!==e&&(t.x=e);const i=t.e;if(null!==i){var n=y,r=R;t.e=null;try{for(var o=0;o{Promise.resolve().then((()=>{var t;if(!e.defaultPrevented)for(const n of e.target.elements)null==(t=n.__on_r)||t.call(n)}))}),{capture:!0}))}function zn(e){var t=R,n=y;Ce(null),de(null);try{return e()}finally{Ce(t),de(n)}}function vl(e,t,n,r=n){e.addEventListener(t,(()=>zn(n)));const o=e.__on_r;e.__on_r=o?()=>{o(),r(!0)}:()=>r(!0),Pn()}const Gn=new Set,Bt=new Set;function hl(e,t,n,r){function o(e){if(r.capture||st.call(t,e),!e.cancelBubble)return zn((()=>n.call(this,e)))}return e.startsWith("pointer")||e.startsWith("touch")||"wheel"===e?rr((()=>{t.addEventListener(e,o,r)})):t.addEventListener(e,o,r),o}function gl(e,t,n,r,o){var l={capture:r,passive:o},i=hl(e,t,n,l);(t===document.body||t===window||t===document)&&Ji((()=>{t.removeEventListener(e,i,l)}))}function _l(e){for(var t=0;ti||r});var d=R,f=y;Ce(null),de(null);try{for(var v,h=[];null!==i;){var p=i.assignedSlot||i.parentNode||i.host||null;try{var g=i["__"+o];if(void 0!==g&&!i.disabled)if(en(g)){var[m,...b]=g;m.apply(i,[e,...b])}else g.call(i,e)}catch(e){v?h.push(e):v=e}if(e.cancelBubble||p===n||null===p)break;i=p}if(v){for(let e of h)queueMicrotask((()=>{throw e}));throw v}}finally{e.__root=n,delete e.currentTarget,Ce(d),de(f)}}}function Dn(e){var t=document.createElement("template");return t.innerHTML=e,t.content}function Ee(e,t){var n=y;null===n.nodes_start&&(n.nodes_start=e,n.nodes_end=t)}function oe(e,t){var n,r=!!(1&t),o=!!(2&t),l=!e.startsWith("");return()=>{if(P)return Ee(V,null),V;void 0===n&&(n=Dn(l?e:""+e),r||(n=Le(n)));var t=o?document.importNode(n,!0):n.cloneNode(!0);r?Ee(Le(t),t.lastChild):Ee(t,t);return t}}function ml(){if(P)return Ee(V,null),V;var e=document.createDocumentFragment(),t=document.createComment(""),n=Nt();return e.append(t,n),Ee(t,n),e}function K(e,t){if(P)return y.nodes_end=V,void He();null!==e&&e.before(t)}const bl=["touchstart","touchmove"];function yl(e){return bl.includes(e)}function Yn(e,t){return Fn(e,t)}function pl(e,t){Wt(),t.intro=t.intro??!1;const n=t.target,r=P,o=V;try{for(var l=Le(n);l&&(8!==l.nodeType||l.data!==Jr);)l=Re(l);if(!l)throw Ue;Ze(!0),ke(l),He();const r=Fn(e,{...t,anchor:l});if(null===V||8!==V.nodeType||V.data!==qr)throw St(),Ue;return Ze(!1),r}catch(r){if(r===Ue)return!1===t.recover&&Gi(),Wt(),Mi(n),Ze(!1),Yn(e,t);throw r}finally{Ze(r),ke(o)}}const Oe=new Map;function Fn(e,{target:t,anchor:n,props:r={},events:o,context:l,intro:i=!0}){Wt();var a=new Set,s=e=>{for(var n=0;n{var i=n??t.appendChild(Nt());return Rt((()=>{l&&(Tn({}),X.c=l);o&&(r.$$events=o),P&&Ee(i,null),c=e(i,r)||{},P&&(y.nodes_end=V),l&&Vn()})),()=>{var e;for(var r of a){t.removeEventListener(r,st);var o=Oe.get(r);0==--o?(document.removeEventListener(r,st),Oe.delete(r)):Oe.set(r,o)}Bt.delete(s),Ht.delete(c),i!==n&&(null==(e=i.parentNode)||e.removeChild(i))}}));return Ht.set(c,u),c}let Ht=new WeakMap;function wl(e){const t=Ht.get(e);t&&t()}function pe(e,t,n=!1){P&&He();var r=e,o=null,l=null,i=j,a=!1;const s=(e,t=!0)=>{a=!0,c(t,e)},c=(e,t)=>{if(i===(i=e))return;let n=!1;if(P){const e=r.data===Kr;!!i===e&&(ke(r=Ui()),Ze(!1),n=!0)}i?(o?Or(o):t&&(o=Rt((()=>t(r)))),l&&Fr(l,(()=>{l=null}))):(l?Or(l):t&&(l=Rt((()=>t(r)))),o&&Fr(o,(()=>{o=null}))),n&&Ze(!0)};tr((()=>{a=!1,t(s),a||c(null,null)}),n?Kt:0),P&&(r=V)}function Xe(e,t,n,r,o){var l,i=e,a="";tr((()=>{a!==(a=t()??"")?(void 0!==l&&(Te(l),l=void 0),""!==a&&(l=Rt((()=>{if(P){V.data;for(var e=He(),t=e;null!==e&&(8!==e.nodeType||""!==e.data);)t=e,e=Re(e);if(null===e)throw St(),Ue;return Ee(V,t),void(i=ke(e))}var n=Dn(a+"");Ee(Le(n),n.lastChild),i.before(n)})))):P&&He()}))}function El(e,t,n,r,o){var l;P&&He();var i=null==(l=t.$$slots)?void 0:l[n],a=!1;!0===i&&(i=t.children,a=!0),void 0===i||i(e,a?()=>r:r)}function xl(e,t){rr((()=>{var n=e.getRootNode(),r=n.host?n:n.head??n.ownerDocument.head;if(!r.querySelector("#"+t.hash)){const e=document.createElement("style");e.id=t.hash,e.textContent=t.code,r.appendChild(e)}}))}function Ur(e){if(P){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute("value")){var n=e.value;ne(e,"value",null),e.value=n}if(e.hasAttribute("checked")){var r=e.checked;ne(e,"checked",null),e.checked=r}}};e.__on_r=n,el(n),Pn()}}function kl(e,t){var n=e.__attributes??(e.__attributes={});n.value===(n.value=t??void 0)||e.value===t&&(0!==t||"PROGRESS"!==e.nodeName)||(e.value=t)}function ne(e,t,n,r){var o=e.__attributes??(e.__attributes={});P&&(o[t]=e.getAttribute(t),"src"===t||"srcset"===t||"href"===t&&"LINK"===e.nodeName)||o[t]!==(o[t]=n)&&("style"===t&&"__styles"in e&&(e.__styles={}),"loading"===t&&(e[Li]=n),null==n?e.removeAttribute(t):"string"!=typeof n&&Cl(e).includes(t)?e[t]=n:e.setAttribute(t,n))}var Mr=new Map;function Cl(e){var t=Mr.get(e.nodeName);if(t)return t;Mr.set(e.nodeName,t=[]);for(var n,r=Xt(e),o=Element.prototype;o!==r;){for(var l in n=Ri(r))n[l].set&&t.push(l);r=Xt(r)}return t}function Rl(e,t,n){if(n){if(e.classList.contains(t))return;e.classList.add(t)}else{if(!e.classList.contains(t))return;e.classList.remove(t)}}function Il(e,t,n=t){vl(e,"change",(t=>{var r=t?e.defaultChecked:e.checked;n(r)})),(P&&e.defaultChecked!==e.checked||null==Je(t))&&n(e.checked),er((()=>{var n=t();e.checked=!!n}))}function jr(e,t){return e===t||(null==e?void 0:e[ut])===t}function Br(e={},t,n,r){return Qt((()=>{var r,o;return er((()=>{r=o,o=[],Je((()=>{e!==n(...o)&&(t(e,...o),r&&jr(n(...r),e)&&t(null,...r))}))})),()=>{rr((()=>{o&&jr(n(...o),e)&&t(null,...o)}))}})),e}function On(e){null===X&&Cn(),Ut((()=>{const t=Je(e);if("function"==typeof t)return t}))}function $l(e){null===X&&Cn(),On((()=>()=>Je(e)))}let yt=!1;function Sl(e){var t=yt;try{return yt=!1,[e(),yt]}finally{yt=t}}function Nl(e){for(var t=y,n=y;null!==t&&!(96&t.f);)t=t.parent;try{return de(t),e()}finally{de(n)}}function N(e,t,n,r){var o,l,i=!!(1&n),a=!sn,s=!!(8&n),c=!!(16&n),u=!1;s?[l,u]=Sl((()=>e[t])):l=e[t];var d,f=ut in e||ln in e,h=(null==(o=Ne(e,t))?void 0:o.set)??(f&&s&&t in e?n=>e[t]=n:void 0),p=r,g=!0,m=!1,y=()=>(m=!0,g&&(g=!1,p=c?Je(r):r),p);if(void 0===l&&void 0!==r&&(h&&a&&Di(),l=y(),h&&h(l)),d=()=>{var n=e[t];return void 0===n?y():(g=!0,m=!1,n)},!(4&n))return d;if(h){var b=e.$$legacy;return function(e,t){return arguments.length>0?((!t||b||u)&&h(t?d():e),e):d()}}var w=!1,x=un(l),$=Nl((()=>ot((()=>{var e=d(),t=v(x);return w?(w=!1,t):x.v=e}))));return i||($.equals=on),function(e,t){if(arguments.length>0){const n=t?v($):s?ie(e):e;return $.equals(n)||(w=!0,T(x,n),m&&void 0!==p&&(p=n),Je((()=>v($)))),e}return v($)}}function Ll(e){return new Al(e)}var _e,te;class Al{constructor(e){var t;Yt(this,_e),Yt(this,te);var n=new Map,r=(e,t)=>{var r=un(t);return n.set(e,r),r};const o=new Proxy({...e.props||{},$$events:{}},{get:(e,t)=>v(n.get(t)??r(t,Reflect.get(e,t))),has:(e,t)=>t===ln||(v(n.get(t)??r(t,Reflect.get(e,t))),Reflect.has(e,t)),set:(e,t,o)=>(T(n.get(t)??r(t,o),o),Reflect.set(e,t,o))});Ft(this,te,(e.hydrate?pl:Yn)(e.component,{target:e.target,anchor:e.anchor,props:o,context:e.context,intro:e.intro??!1,recover:e.recover})),(!(null!=(t=null==e?void 0:e.props)&&t.$$host)||!1===e.sync)&&k(),Ft(this,_e,o.$$events);for(const e of Object.keys(M(this,te)))"$set"===e||"$destroy"===e||"$on"===e||kt(this,e,{get(){return M(this,te)[e]},set(t){M(this,te)[e]=t},enumerable:!0});M(this,te).$set=e=>{Object.assign(o,e)},M(this,te).$destroy=()=>{wl(M(this,te))}}$set(e){M(this,te).$set(e)}$on(e,t){M(this,_e)[e]=M(this,_e)[e]||[];const n=(...e)=>t.call(this,...e);return M(this,_e)[e].push(n),()=>{M(this,_e)[e]=M(this,_e)[e].filter((e=>e!==n))}}$destroy(){M(this,te).$destroy()}}let Xn;function Et(e,t,n,r){var o;const l=null==(o=n[e])?void 0:o.type;if(t="Boolean"===l&&"boolean"!=typeof t?null!=t:t,!r||!n[e])return t;if("toAttribute"===r)switch(l){case"Object":case"Array":return null==t?null:JSON.stringify(t);case"Boolean":return t?"":null;case"Number":return t??null;default:return t}else switch(l){case"Object":case"Array":return t&&JSON.parse(t);case"Boolean":default:return t;case"Number":return null!=t?+t:t}}function Tl(e){const t={};return e.childNodes.forEach((e=>{t[e.slot||"default"]=!0})),t}function Vl(e,t,n,r,o,l){let i=class extends Xn{constructor(){super(e,n,o),this.$$p_d=t}static get observedAttributes(){return xt(t).map((e=>(t[e].attribute||e).toLowerCase()))}};return xt(t).forEach((e=>{kt(i.prototype,e,{get(){return this.$$c&&e in this.$$c?this.$$c[e]:this.$$d[e]},set(n){var r;n=Et(e,n,t),this.$$d[e]=n;var o=this.$$c;o&&((null==(r=Ne(o,e))?void 0:r.get)?o[e]=n:o.$set({[e]:n}))}})})),r.forEach((e=>{kt(i.prototype,e,{get(){var t;return null==(t=this.$$c)?void 0:t[e]}})})),e.element=i,i}_e=new WeakMap,te=new WeakMap,"function"==typeof HTMLElement&&(Xn=class extends HTMLElement{constructor(e,t,n){super(),re(this,"$$ctor"),re(this,"$$s"),re(this,"$$c"),re(this,"$$cn",!1),re(this,"$$d",{}),re(this,"$$r",!1),re(this,"$$p_d",{}),re(this,"$$l",{}),re(this,"$$l_u",new Map),re(this,"$$me"),this.$$ctor=e,this.$$s=t,n&&this.attachShadow({mode:"open"})}addEventListener(e,t,n){if(this.$$l[e]=this.$$l[e]||[],this.$$l[e].push(t),this.$$c){const n=this.$$c.$on(e,t);this.$$l_u.set(t,n)}super.addEventListener(e,t,n)}removeEventListener(e,t,n){if(super.removeEventListener(e,t,n),this.$$c){const e=this.$$l_u.get(t);e&&(e(),this.$$l_u.delete(t))}}async connectedCallback(){if(this.$$cn=!0,!this.$$c){let e=function(e){return t=>{const n=document.createElement("slot");"default"!==e&&(n.name=e),K(t,n)}};if(await Promise.resolve(),!this.$$cn||this.$$c)return;const t={},n=Tl(this);for(const r of this.$$s)r in n&&("default"!==r||this.$$d.children?t[r]=e(r):(this.$$d.children=e(r),t.default=!0));for(const e of this.attributes){const t=this.$$g_p(e.name);t in this.$$d||(this.$$d[t]=Et(t,e.value,this.$$p_d,"toProp"))}for(const e in this.$$p_d)!(e in this.$$d)&&void 0!==this[e]&&(this.$$d[e]=this[e],delete this[e]);this.$$c=Ll({component:this.$$ctor,target:this.shadowRoot||this,props:{...this.$$d,$$slots:t,$$host:this}}),this.$$me=_n((()=>{er((()=>{var e;this.$$r=!0;for(const t of xt(this.$$c)){if(null==(e=this.$$p_d[t])||!e.reflect)continue;this.$$d[t]=this.$$c[t];const n=Et(t,this.$$d[t],this.$$p_d,"toAttribute");null==n?this.removeAttribute(this.$$p_d[t].attribute||t):this.setAttribute(this.$$p_d[t].attribute||t,n)}this.$$r=!1}))}));for(const e in this.$$l)for(const t of this.$$l[e]){const n=this.$$c.$on(e,t);this.$$l_u.set(t,n)}this.$$l={}}}attributeChangedCallback(e,t,n){var r;this.$$r||(e=this.$$g_p(e),this.$$d[e]=Et(e,n,this.$$p_d,"toProp"),null==(r=this.$$c)||r.$set({[e]:this.$$d[e]}))}disconnectedCallback(){this.$$cn=!1,Promise.resolve().then((()=>{!this.$$cn&&this.$$c&&(this.$$c.$destroy(),this.$$me(),this.$$c=void 0)}))}$$g_p(e){return xt(this.$$p_d).find((t=>this.$$p_d[t].attribute===e||!this.$$p_d[t].attribute&&t.toLowerCase()===e))||e}});const Zn=new TextEncoder;function Pl(e){return[...new Uint8Array(e)].map((e=>e.toString(16).padStart(2,"0"))).join("")}async function zl(e,t="SHA-256",n=1e5){const r=Date.now().toString(16);e||(e=Math.round(Math.random()*n));return{algorithm:t,challenge:await Wn(r,e,t),salt:r,signature:""}}async function Wn(e,t,n){if(typeof crypto>"u"||!("subtle"in crypto)||!("digest"in crypto.subtle))throw new Error("Web Crypto is not available. Secure context is required (https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).");return Pl(await crypto.subtle.digest(n.toUpperCase(),Zn.encode(e+t)))}function Gl(e,t,n="SHA-256",r=1e6,o=0){const l=new AbortController,i=Date.now();return{promise:(async()=>{for(let a=o;a<=r;a+=1){if(l.signal.aborted)return null;if(await Wn(t,a,n)===e)return{number:a,took:Date.now()-i}}return null})(),controller:l}}function Dl(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{}}function Yl(e){const t=atob(e),n=new Uint8Array(t.length);for(let e=0;e{for(let e=r;e<=n;e+=1){if(l.signal.aborted||!a||!s)return null;try{const t=await crypto.subtle.decrypt({name:o,iv:Fl(e)},a,s);if(t)return{clearText:(new TextDecoder).decode(t),took:Date.now()-i}}catch{}}return null})(),controller:l}}var x=(e=>(e.ERROR="error",e.VERIFIED="verified",e.VERIFYING="verifying",e.UNVERIFIED="unverified",e.EXPIRED="expired",e))(x||{}),Xl=oe(''),Zl=oe(' ',1),Wl=oe(''),Ul=oe(''),Ml=oe('
'),jl=oe('
'),Bl=oe('
'),Hl=oe('
'),Jl=oe(''),Kl=oe('
'),ql=oe('
',1);const Ql={hash:"svelte-ddsc3z",code:'.altcha.svelte-ddsc3z {background:var(--altcha-color-base, transparent);border:var(--altcha-border-width, 1px) solid var(--altcha-color-border, #a0a0a0);border-radius:var(--altcha-border-radius, 3px);color:var(--altcha-color-text, currentColor);display:flex;flex-direction:column;max-width:var(--altcha-max-width, 260px);position:relative;text-align:left;}.altcha.svelte-ddsc3z:focus-within {border-color:var(--altcha-color-border-focus, currentColor);}.altcha[data-floating].svelte-ddsc3z {background:var(--altcha-color-base, white);display:none;filter:drop-shadow(3px 3px 6px rgba(0, 0, 0, 0.2));left:-100%;position:fixed;top:-100%;width:var(--altcha-max-width, 260px);z-index:999999;}.altcha[data-floating=top].svelte-ddsc3z .altcha-anchor-arrow:where(.svelte-ddsc3z) {border-bottom-color:transparent;border-top-color:var(--altcha-color-border, #a0a0a0);bottom:-12px;top:auto;}.altcha[data-floating=bottom].svelte-ddsc3z:focus-within::after {border-bottom-color:var(--altcha-color-border-focus, currentColor);}.altcha[data-floating=top].svelte-ddsc3z:focus-within::after {border-top-color:var(--altcha-color-border-focus, currentColor);}.altcha[data-floating].svelte-ddsc3z:not([data-state=unverified]) {display:block;}.altcha-anchor-arrow.svelte-ddsc3z {border:6px solid transparent;border-bottom-color:var(--altcha-color-border, #a0a0a0);content:"";height:0;left:12px;position:absolute;top:-12px;width:0;}.altcha-main.svelte-ddsc3z {align-items:center;display:flex;gap:0.4rem;padding:0.7rem;}.altcha-label.svelte-ddsc3z {flex-grow:1;}.altcha-label.svelte-ddsc3z label:where(.svelte-ddsc3z) {cursor:pointer;}.altcha-logo.svelte-ddsc3z {color:currentColor;opacity:0.3;}.altcha-logo.svelte-ddsc3z:hover {opacity:1;}.altcha-error.svelte-ddsc3z {color:var(--altcha-color-error-text, #f23939);display:flex;font-size:0.85rem;gap:0.3rem;padding:0 0.7rem 0.7rem;}.altcha-footer.svelte-ddsc3z {align-items:center;background-color:var(--altcha-color-footer-bg, transparent);display:flex;font-size:0.75rem;opacity:0.4;padding:0.2rem 0.7rem;text-align:right;}.altcha-footer.svelte-ddsc3z:hover {opacity:1;}.altcha-footer.svelte-ddsc3z > :where(.svelte-ddsc3z):first-child {flex-grow:1;}.altcha-footer.svelte-ddsc3z a {color:currentColor;}.altcha-checkbox.svelte-ddsc3z {display:flex;align-items:center;height:24px;width:24px;}.altcha-checkbox.svelte-ddsc3z input:where(.svelte-ddsc3z) {width:18px;height:18px;margin:0;}.altcha-hidden.svelte-ddsc3z {display:none;}.altcha-spinner.svelte-ddsc3z {\n animation: svelte-ddsc3z-altcha-spinner 0.75s infinite linear;transform-origin:center;}\n\n@keyframes svelte-ddsc3z-altcha-spinner {\n 100% {\n transform: rotate(360deg);\n }\n}'};function ea(e,t){var n,r;Tn(t,!0),xl(e,Ql);let o=N(t,"auto",7,void 0),l=N(t,"blockspam",7,void 0),i=N(t,"challengeurl",7,void 0),a=N(t,"challengejson",7,void 0),s=N(t,"customfetch",7,void 0),c=N(t,"debug",7,!1),u=N(t,"delay",7,0),d=N(t,"expire",7,void 0),f=N(t,"floating",7,void 0),h=N(t,"floatinganchor",7,void 0),p=N(t,"floatingoffset",7,void 0),g=N(t,"hidefooter",7,!1),m=N(t,"hidelogo",7,!1),y=N(t,"name",7,"altcha"),b=N(t,"maxnumber",7,1e6),w=N(t,"mockerror",7,!1),$=N(t,"obfuscated",7,void 0),E=N(t,"plugins",7,void 0),R=N(t,"refetchonexpire",7,!0),C=N(t,"spamfilter",7,!1),_=N(t,"strings",7,void 0),I=N(t,"test",7,!1),L=N(t,"verifyurl",7,void 0),V=N(t,"workers",23,(()=>Math.min(16,navigator.hardwareConcurrency||8))),X=N(t,"workerurl",7,void 0);const S=["SHA-256","SHA-384","SHA-512"],P="Visit Altcha.org",z="https://altcha.org/",O=(e,n)=>{t.$$host.dispatchEvent(new CustomEvent(e,{detail:n}))},j=null==(r=null==(n=document.documentElement.lang)?void 0:n.split("-"))?void 0:r[0],G=ot((()=>{var e;return i()&&new URL(i(),location.origin).host.endsWith(".altcha.org")&&!(null==(e=i())||!e.includes("apiKey=ckey_"))})),Z=ot((()=>a()?he(a()):void 0)),A=ot((()=>_()?he(_()):{})),W=ot((()=>{var e;return{ariaLinkLabel:P,error:"Verification failed. Try again later.",expired:"Verification expired. Try again.",footer:`Protected by ALTCHA`,label:"I'm not a robot",verified:"Verified",verifying:"Verifying...",waitAlert:"Verifying... please wait.",...v(A)}}));let M=Ye(!1),Y=Ye(ie(x.UNVERIFIED)),B=Ye(void 0),F=Ye(null),U=null,D=null,q=Ye(null),Q=null,ee=[],te=Ye(null);function re(e,t){return btoa(JSON.stringify({algorithm:e.algorithm,challenge:e.challenge,number:t.number,salt:e.salt,signature:e.signature,test:!!I()||void 0,took:t.took}))}function oe(){i()&&R()&&v(Y)===x.VERIFIED?Ve():_e(x.EXPIRED,v(W).expired)}function le(...e){(c()||e.some((e=>e instanceof Error)))&&console[e[0]instanceof Error?"error":"log"]("ALTCHA",`[name=${y()}]`,...e)}function ae(e){const t=e.target;f()&&t&&!v(B).contains(t)&&(v(Y)===x.VERIFIED||"off"===o()&&v(Y)===x.UNVERIFIED)&&(v(B).style.display="none")}function se(){f()&&v(Y)!==x.UNVERIFIED&&me()}function ce(e){v(Y)===x.UNVERIFIED&&Ve()}function ue(e){D&&"onsubmit"===o()?v(Y)===x.UNVERIFIED?(e.preventDefault(),e.stopPropagation(),Ve().then((()=>{null==D||D.requestSubmit()}))):v(Y)!==x.VERIFIED&&(e.preventDefault(),e.stopPropagation(),v(Y)===x.VERIFYING&&fe()):D&&f()&&"off"===o()&&v(Y)===x.UNVERIFIED&&(e.preventDefault(),e.stopPropagation(),v(B).style.display="block",me())}function de(){_e()}function fe(){v(Y)===x.VERIFYING&&v(W).waitAlert&&alert(v(W).waitAlert)}function ve(){f()&&me()}function he(e){return JSON.parse(e)}function me(e=20){if(v(B))if(U||(U=(h()?document.querySelector(h()):null==D?void 0:D.querySelector('input[type="submit"], button[type="submit"], button:not([type="button"]):not([type="reset"])'))||D),U){const t=parseInt(p(),10)||12,n=U.getBoundingClientRect(),r=v(B).getBoundingClientRect(),o=document.documentElement.clientHeight,l=document.documentElement.clientWidth,i="auto"===f()?n.bottom+r.height+t+e>o:"top"===f(),a=Math.max(e,Math.min(l-e-r.width,n.left+n.width/2-r.width/2));if(v(B).style.top=i?n.top-(r.height+t)+"px":`${n.bottom+t}px`,v(B).style.left=`${a}px`,v(B).setAttribute("data-floating",i?"top":"bottom"),v(F)){const e=v(F).getBoundingClientRect();v(F).style.left=n.left-a+n.width/2-e.width/2+"px"}}else le("unable to find floating anchor element")}async function ye(e){if(!L())throw new Error("Attribute verifyurl not set.");le("requesting server verification from",L());const t={payload:e};if(!1!==C()){const{blockedCountries:e,classifier:n,disableRules:r,email:o,expectedLanguages:l,expectedCountries:i,fields:a,ipAddress:s,text:c,timeZone:u}="ipAddress"===C()?{blockedCountries:void 0,classifier:void 0,disableRules:void 0,email:!1,expectedCountries:void 0,expectedLanguages:void 0,fields:!1,ipAddress:void 0,text:void 0,timeZone:void 0}:"object"==typeof C()?C():{blockedCountries:void 0,classifier:void 0,disableRules:void 0,email:void 0,expectedCountries:void 0,expectedLanguages:void 0,fields:void 0,ipAddress:void 0,text:void 0,timeZone:void 0};t.blockedCountries=e,t.classifier=n,t.disableRules=r,t.email=!1===o?void 0:function(e){var t;const n=null==D?void 0:D.querySelector("string"==typeof e?`input[name="${e}"]`:'input[type="email"]:not([data-no-spamfilter])');return(null==(t=null==n?void 0:n.value)?void 0:t.slice(n.value.indexOf("@")))||void 0}(o),t.expectedCountries=i,t.expectedLanguages=l||(j?[j]:void 0),t.fields=!1===a?void 0:function(e){return[...(null==D?void 0:D.querySelectorAll(null!=e&&e.length?e.map((e=>`input[name="${e}"]`)).join(", "):'input[type="text"]:not([data-no-spamfilter]), textarea:not([data-no-spamfilter])'))||[]].reduce(((e,t)=>{const n=t.name,r=t.value;return n&&r&&(e[n]=/\n/.test(r)?r.replace(new RegExp("(?{const l=n*o;return new Promise((n=>{t.addEventListener("message",(e=>{if(e.data)for(const e of r)e!==t&&e.postMessage({type:"abort"});n(e.data)})),t.postMessage({payload:e,max:l+o,start:l,type:"work"})}))})));for(const e of r)e.terminate();return l.find((e=>!!e))||null}(e,e.maxnumber)}catch(e){le(e)}if(void 0!==(null==t?void 0:t.number)||"obfuscated"in e)return{data:e,solution:t}}if("obfuscated"in e){const t=await Ol(e.obfuscated,e.key,e.maxnumber);return{data:e,solution:await t.promise}}return{data:e,solution:await Gl(e.challenge,e.salt,e.algorithm,e.maxnumber||b()).promise}}async function ke(){if(!$())return void Le(x.ERROR);const e=ee.find((e=>"obfuscation"===e.constructor.pluginName));return e&&"clarify"in e?"clarify"in e&&"function"==typeof e.clarify?e.clarify():void 0:(Le(x.ERROR),void le("Plugin `obfuscation` not found. Import `altcha/plugins/obfuscation` to load it."))}function Ee(e){void 0!==e.obfuscated&&$(e.obfuscated),void 0!==e.auto&&(o(e.auto),"onload"===o()&&($()?ke():Ve())),void 0!==e.blockspam&&l(!!e.blockspam),void 0!==e.customfetch&&s(e.customfetch),void 0!==e.floatinganchor&&h(e.floatinganchor),void 0!==e.delay&&u(e.delay),void 0!==e.floatingoffset&&p(e.floatingoffset),void 0!==e.floating&&we(e.floating),void 0!==e.expire&&(be(e.expire),d(e.expire)),e.challenge&&(a("string"==typeof e.challenge?e.challenge:JSON.stringify(e.challenge)),xe(v(Z))),void 0!==e.challengeurl&&i(e.challengeurl),void 0!==e.debug&&c(!!e.debug),void 0!==e.hidefooter&&g(!!e.hidefooter),void 0!==e.hidelogo&&m(!!e.hidelogo),void 0!==e.maxnumber&&b(+e.maxnumber),void 0!==e.mockerror&&w(!!e.mockerror),void 0!==e.name&&y(e.name),void 0!==e.refetchonexpire&&R(!!e.refetchonexpire),void 0!==e.spamfilter&&C("object"==typeof e.spamfilter?e.spamfilter:!!e.spamfilter),e.strings&&_("string"==typeof e.strings?e.strings:JSON.stringify(e.strings)),void 0!==e.test&&I("number"==typeof e.test?e.test:!!e.test),void 0!==e.verifyurl&&L(e.verifyurl),void 0!==e.workers&&V(+e.workers),void 0!==e.workerurl&&X(e.workerurl)}function Re(){return{auto:o(),blockspam:l(),challengeurl:i(),debug:c(),delay:u(),expire:d(),floating:f(),floatinganchor:h(),floatingoffset:p(),hidefooter:g(),hidelogo:m(),name:y(),maxnumber:b(),mockerror:w(),obfuscated:$(),refetchonexpire:R(),spamfilter:C(),strings:v(W),test:I(),verifyurl:L(),workers:V(),workerurl:X()}}function Ce(){return U}function Ne(){return v(Y)}function _e(e=x.UNVERIFIED,t=null){Q&&(clearTimeout(Q),Q=null),T(M,!1),T(te,null),Le(e,t)}function Ie(e){U=e}function Le(e,t=null){T(Y,ie(e)),T(q,ie(t)),O("statechange",{payload:v(te),state:v(Y)})}async function Ve(){return _e(x.VERIFYING),await new Promise((e=>setTimeout(e,u()||0))),async function(){var e;if(w())throw le("mocking error"),new Error("Mocked error.");if(v(Z))return le("using provided json data"),v(Z);if(I())return le("generating test challenge",{test:I()}),zl("boolean"!=typeof I()?+I():void 0);{if(!i()&&D){const e=D.getAttribute("action");null!=e&&e.includes("/form/")&&i(e+"/altcha")}if(!i())throw new Error("Attribute challengeurl not set.");le("fetching challenge from",i());let t=null,n=null;if(s())if(le("using customfetch"),"string"==typeof s()){if(t=globalThis[s()]||null,!t)throw new Error(`Custom fetch function not found: ${s()}`)}else t=s();const r={headers:!1!==C()?{"x-altcha-spam-filter":"1"}:{}};if(t){if(n=await t(i(),r),!(n&&n instanceof Response))throw new Error("Custom fetch function did not return a response.")}else n=await fetch(i(),r);if(200!==n.status)throw new Error(`Server responded with ${n.status}.`);const o=n.headers.get("X-Altcha-Config"),l=await n.json(),a=new URLSearchParams(null==(e=l.salt.split("?"))?void 0:e[1]),c=a.get("expires")||a.get("expire");if(c){const e=new Date(1e3*+c),t=isNaN(e.getTime())?0:e.getTime()-Date.now();t>0&&be(t)}if(o)try{const e=JSON.parse(o);e&&"object"==typeof e&&(e.verifyurl&&(e.verifyurl=new URL(e.verifyurl,new URL(i())).toString()),Ee(e))}catch(e){le("unable to configure from X-Altcha-Config",e)}return l}}().then((e=>(xe(e),le("challenge",e),$e(e)))).then((({data:e,solution:t})=>{if(le("solution",t),"challenge"in e&&t&&!("clearText"in t)){if(void 0===(null==t?void 0:t.number))throw le("Unable to find a solution. Ensure that the 'maxnumber' attribute is greater than the randomly generated number."),new Error("Unexpected result returned.");if(L())return ye(re(e,t));T(te,ie(re(e,t))),le("payload",v(te))}})).then((()=>{Le(x.VERIFIED),le("verified"),cl().then((()=>{O("verified",{payload:v(te)})}))})).catch((e=>{le(e),Le(x.ERROR,e.message)}))}Ut((()=>{!function(){for(const e of ee)"function"==typeof e.onErrorChange&&e.onErrorChange(v(q))}(v(q))})),Ut((()=>{!function(){for(const e of ee)"function"==typeof e.onStateChange&&e.onStateChange(v(Y));f()&&v(Y)!==x.UNVERIFIED&&requestAnimationFrame((()=>{me()})),T(M,v(Y)===x.VERIFIED)}(v(Y))})),$l((()=>{(function(){for(const e of ee)e.destroy()})(),D&&(D.removeEventListener("submit",ue),D.removeEventListener("reset",de),D.removeEventListener("focusin",ce),D=null),Q&&(clearTimeout(Q),Q=null),document.removeEventListener("click",ae),document.removeEventListener("scroll",se),window.removeEventListener("resize",ve)})),On((()=>{var e;le("mounted","1.2.0"),le("workers",V()),function(){const e=void 0!==E()?E().split(","):void 0;for(const t of globalThis.altchaPlugins)(!e||e.includes(t.pluginName))&&ee.push(new t({el:v(B),clarify:ke,dispatch:O,getConfiguration:Re,getFloatingAnchor:Ce,getState:Ne,log:le,reset:_e,solve:$e,setState:Le,setFloatingAnchor:Ie,verify:Ve}))}(),le("plugins",ee.length?ee.map((e=>e.constructor.pluginName)).join(", "):"none"),I()&&le("using test mode"),d()&&be(d()),void 0!==o()&&le("auto",o()),void 0!==f()&&we(f()),D=null==(e=v(B))?void 0:e.closest("form"),D&&(D.addEventListener("submit",ue,{capture:!0}),D.addEventListener("reset",de),"onfocus"===o()&&D.addEventListener("focusin",ce)),"onload"===o()&&($()?ke():Ve()),v(G)&&(g()||m())&&le("Attributes hidefooter and hidelogo ignored because usage with free API Keys requires attribution."),requestAnimationFrame((()=>{O("load")}))}));var Se=ql(),Te=Ot(Se);El(Te,t,"default",{});var Pe=ge(Te,2),ze=J(Pe),Oe=J(ze),je=e=>{K(e,Xl())};pe(Oe,(e=>{v(Y)===x.VERIFYING&&e(je)}));var Ge=ge(Oe,2),Ze=J(Ge);Ur(Ze),Ze.__change=function(){[x.UNVERIFIED,x.ERROR,x.EXPIRED].includes(v(Y))?!1!==C()&&!1===(null==D?void 0:D.reportValidity())?T(M,!1):$()?ke():Ve():T(M,!0)},H(Ge);var Ae=ge(Ge,2),We=J(Ae),Me=e=>{var t=Zl(),n=Ot(t);Xe(J(n),(()=>v(W).verified)),H(n);var r=ge(n,2);Ur(r),Fe((()=>{ne(r,"name",y()),kl(r,v(te))})),K(e,t)},Je=e=>{var t=ml(),n=Ot(t),r=e=>{var t=Wl();Xe(J(t),(()=>v(W).verifying)),H(t),K(e,t)},o=e=>{var t=Ul();Xe(J(t),(()=>v(W).label)),H(t),Fe((()=>ne(t,"for",`${y()??""}_checkbox`))),K(e,t)};pe(n,(e=>{v(Y)===x.VERIFYING?e(r):e(o,!1)}),!0),K(e,t)};pe(We,(e=>{v(Y)===x.VERIFIED?e(Me):e(Je,!1)})),H(Ae);var Be=ge(Ae,2),He=e=>{var t=Ml(),n=J(t);ne(n,"href",z),H(t),Fe((()=>ne(n,"aria-label",v(W).ariaLinkLabel))),K(e,t)};pe(Be,(e=>{(!0!==m()||v(G))&&e(He)})),H(ze);var Ue=ge(ze,2),Ke=e=>{var t=Hl(),n=ge(J(t),2),r=e=>{var t=jl();Xe(J(t),(()=>v(W).expired)),H(t),Fe((()=>ne(t,"title",v(q)))),K(e,t)},o=e=>{var t=Bl();Xe(J(t),(()=>v(W).error)),H(t),Fe((()=>ne(t,"title",v(q)))),K(e,t)};pe(n,(e=>{v(Y)===x.EXPIRED?e(r):e(o,!1)})),H(t),K(e,t)};pe(Ue,(e=>{(v(q)||v(Y)===x.EXPIRED)&&e(Ke)}));var De=ge(Ue,2),qe=e=>{var t=Jl(),n=J(t);Xe(J(n),(()=>v(W).footer)),H(n),H(t),K(e,t)};pe(De,(e=>{v(W).footer&&(!0!==g()||v(G))&&e(qe)}));var Qe=ge(De,2),et=e=>{var t=Kl();Br(t,(e=>T(F,e)),(()=>v(F))),K(e,t)};return pe(Qe,(e=>{f()&&e(et)})),H(Pe),Br(Pe,(e=>T(B,e)),(()=>v(B))),Fe((()=>{ne(Pe,"data-state",v(Y)),ne(Pe,"data-floating",f()),Rl(Ge,"altcha-hidden",v(Y)===x.VERIFYING),ne(Ze,"id",`${y()??""}_checkbox`),Ze.required="onsubmit"!==o()&&(!f()||"off"!==o())})),gl("invalid",Ze,fe),Il(Ze,(()=>v(M)),(e=>T(M,e))),K(e,Se),Vn({clarify:ke,configure:Ee,getConfiguration:Re,getFloatingAnchor:Ce,getPlugin:function(e){return ee.find((t=>t.constructor.pluginName===e))},getState:Ne,reset:_e,setFloatingAnchor:Ie,setState:Le,verify:Ve,get auto(){return o()},set auto(e=void 0){o(e),k()},get blockspam(){return l()},set blockspam(e=void 0){l(e),k()},get challengeurl(){return i()},set challengeurl(e=void 0){i(e),k()},get challengejson(){return a()},set challengejson(e=void 0){a(e),k()},get customfetch(){return s()},set customfetch(e=void 0){s(e),k()},get debug(){return c()},set debug(e=!1){c(e),k()},get delay(){return u()},set delay(e=0){u(e),k()},get expire(){return d()},set expire(e=void 0){d(e),k()},get floating(){return f()},set floating(e=void 0){f(e),k()},get floatinganchor(){return h()},set floatinganchor(e=void 0){h(e),k()},get floatingoffset(){return p()},set floatingoffset(e=void 0){p(e),k()},get hidefooter(){return g()},set hidefooter(e=!1){g(e),k()},get hidelogo(){return m()},set hidelogo(e=!1){m(e),k()},get name(){return y()},set name(e="altcha"){y(e),k()},get maxnumber(){return b()},set maxnumber(e=1e6){b(e),k()},get mockerror(){return w()},set mockerror(e=!1){w(e),k()},get obfuscated(){return $()},set obfuscated(e=void 0){$(e),k()},get plugins(){return E()},set plugins(e=void 0){E(e),k()},get refetchonexpire(){return R()},set refetchonexpire(e=!0){R(e),k()},get spamfilter(){return C()},set spamfilter(e=!1){C(e),k()},get strings(){return _()},set strings(e=void 0){_(e),k()},get test(){return I()},set test(e=!1){I(e),k()},get verifyurl(){return L()},set verifyurl(e=void 0){L(e),k()},get workers(){return V()},set workers(e=Math.min(16,navigator.hardwareConcurrency||8)){V(e),k()},get workerurl(){return X()},set workerurl(e=void 0){X(e),k()}})}_l(["change"]),customElements.define("altcha-widget",Vl(ea,{blockspam:{type:"Boolean"},debug:{type:"Boolean"},delay:{type:"Number"},expire:{type:"Number"},floatingoffset:{type:"Number"},hidefooter:{type:"Boolean"},hidelogo:{type:"Boolean"},maxnumber:{type:"Number"},mockerror:{type:"Boolean"},refetchonexpire:{type:"Boolean"},test:{type:"Boolean"},workers:{type:"Number"},auto:{},challengeurl:{},challengejson:{},customfetch:{},floating:{},floatinganchor:{},name:{},obfuscated:{},plugins:{},spamfilter:{},strings:{},verifyurl:{},workerurl:{}},["default"],["clarify","configure","getConfiguration","getFloatingAnchor","getPlugin","getState","reset","setFloatingAnchor","setState","verify"],!1)),globalThis.altchaCreateWorker=e=>e?new Worker(new URL(e)):new mi,globalThis.altchaPlugins=globalThis.altchaPlugins||[];export{ea as Altcha}; +//# sourceMappingURL=/sm/7ce2c3fa01e00c401a0636d86d5417d8ec8299f0b98373641f7dc6eae1bf9934.map \ No newline at end of file diff --git a/public/js/maho-captcha.js b/public/js/maho-captcha.js new file mode 100644 index 000000000..1c6c2cbd9 --- /dev/null +++ b/public/js/maho-captcha.js @@ -0,0 +1,162 @@ +/** + * Maho + * + * @package Maho_Captcha + * @copyright Copyright (c) 2025 Maho (https://mahocommerce.com) + * @license https://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + */ + +const MahoCaptcha = { + altchaWidget: null, + altchaState: null, + frontendSelectors: '', + onVerifiedCallback: null, + + async setup(config) { + this.altchaWidget = document.querySelector('altcha-widget'); + this.frontendSelectors = config.frontendSelectors ?? ''; + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', this.initForms.bind(this)); + } else { + this.initForms(); + } + }, + + initForms() { + for (const formEl of document.querySelectorAll(this.frontendSelectors)) { + formEl.addEventListener('focusin', this.loadAltchaScripts.bind(this), true); + formEl.addEventListener('submit', this.onFormSubmit.bind(this), true); + } + }, + + async loadAltchaScripts() { + // Load Altcha JS + await Promise.all([ + new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = '/js/altcha.min.js'; + script.type = 'module'; + script.onload = resolve; + script.onerror = () => reject(`${script.src} Not Found`); + document.head.appendChild(script); + }), + ]); + + // Inject Stylesheet + if (!document.querySelector('style[maho-captcha-style]')) { + const styleEl = document.createElement('style'); + styleEl.dataset.mahoCaptchaStyle = ''; + styleEl.textContent = ` + altcha-widget[name=maho_captcha] { + display: flex; + position: fixed; + bottom: 0; + right: 0; + } + dialog.maho-captcha-verifying { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + padding: 8px; + border: none; + border-radius: 6px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + body:has(&) { + overflow: hidden; + } + &::backdrop { + background: rgba(0, 0, 0, 0.7); + } + } + `; + document.head.appendChild(styleEl); + } + + this.altchaWidget.addEventListener('load', () => { + const state = this.altchaWidget.getState(); + this.onStateChange({ detail: { state } }); + this.altchaWidget.addEventListener('statechange', this.onStateChange.bind(this)); + }); + }, + + onFormSubmit(event) { + const formEl = event.target; + if (this.altchaState !== 'verified') { + event.preventDefault(); + event.stopPropagation(); + + this.showLoader(); + this.onVerifiedCallback = () => { + this.hideLoader(); + formEl.requestSubmit(event.submitter) + } + this.startVerification(); + } + }, + + startVerification() { + if (this.altchaState === 'unverified' || this.altchaState === 'error') { + this.altchaWidget.verify(); + } + }, + + onStateChange(event) { + const { state, payload } = event.detail; + this.altchaState = state; + + // Fix for error `An invalid form control with name='' is not focusable.` + document.getElementById('maho_captcha_checkbox').disabled = state === 'verifying'; + + // Replicate maho_captcha input into all forms + if (state === 'verified' && typeof payload === 'string') { + for (const formEl of document.querySelectorAll(this.frontendSelectors)) { + this.setHiddenInputValue(formEl, payload); + } + } + + // Call stored form submit event on the next event loop + if (state === 'verified' && typeof this.onVerifiedCallback === 'function') { + setTimeout(this.onVerifiedCallback.bind(this), 0); + } + this.onVerifiedCallback = null; + }, + + setHiddenInputValue(formEl, payload) { + let hiddenInput = formEl.querySelector('input[name="maho_captcha"]'); + if (!hiddenInput) { + hiddenInput = document.createElement('input'); + hiddenInput.setAttribute('type', 'hidden'); + hiddenInput.setAttribute('name', 'maho_captcha'); + formEl.appendChild(hiddenInput); + } + hiddenInput.value = payload; + }, + + showLoader() { + if (this.loaderEl) { + return; + } + this.loaderEl = document.createElement('dialog'); + this.loaderEl.className = 'maho-captcha-verifying'; + this.loaderEl.textContent = 'Verifying...'; + this.loaderEl.addEventListener('close', () => { + this.onVerifiedCallback = null; + this.hideLoader(); + }); + document.body.appendChild(this.loaderEl); + this.loaderEl.showModal(); + }, + + hideLoader() { + if (this.loaderEl) { + this.loaderEl.remove(); + this.loaderEl = null; + } + }, +}