From 7847d15da1f1f5f0ec98db7aba896278fe18f083 Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Fri, 7 Mar 2025 09:55:15 +0300 Subject: [PATCH] resolved #31, resolved #32, resolved #33, resolved #35 --- .gitignore | 1 + composer.json | 1 + composer.lock | 222 +- examples/arangodb/collections/menu/index.json | 6 +- mirzaev/huesos/system/models/telegram.php | 6 +- .../system/public/js/modules/catalog.mjs | 1793 ++++++++++++----- .../system/public/js/modules/loader.mjs | 35 +- .../system/public/js/modules/telegram.mjs | 5 + mirzaev/huesos/system/public/js/telegram.js | 16 +- .../public/themes/default/css/catalog.css | 11 +- .../public/themes/default/css/icons/house.css | 52 + 11 files changed, 1429 insertions(+), 719 deletions(-) create mode 100644 mirzaev/huesos/system/public/themes/default/css/icons/house.css diff --git a/.gitignore b/.gitignore index 22d0d82..022b988 100755 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +node_modules vendor diff --git a/composer.json b/composer.json index bd0e6d7..3eaedec 100755 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "require": { "php": "^8.4", "ext-gd": "^8.4", + "ext-intl": "^8.4", "triagens/arangodb": "^3.8", "mirzaev/minimal": "^3.4.0", "mirzaev/arangodb": "^2", diff --git a/composer.lock b/composer.lock index cae7b2f..91b7350 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e4632693e41625716be26163a7e06ecc", + "content-hash": "d59bfe9a2623bc1b6bd95caaf90e9127", "packages": [ { "name": "avadim/fast-excel-helper", @@ -63,16 +63,16 @@ }, { "name": "avadim/fast-excel-reader", - "version": "v2.23.0", + "version": "v2.24.0", "source": { "type": "git", "url": "https://github.com/aVadim483/fast-excel-reader.git", - "reference": "581ca926d359f18043faec7dc7d8bea654b1d8e4" + "reference": "8afafb92817705a6bfd1c9bc53b0083d9d36e7c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aVadim483/fast-excel-reader/zipball/581ca926d359f18043faec7dc7d8bea654b1d8e4", - "reference": "581ca926d359f18043faec7dc7d8bea654b1d8e4", + "url": "https://api.github.com/repos/aVadim483/fast-excel-reader/zipball/8afafb92817705a6bfd1c9bc53b0083d9d36e7c1", + "reference": "8afafb92817705a6bfd1c9bc53b0083d9d36e7c1", "shasum": "" }, "require": { @@ -114,9 +114,9 @@ ], "support": { "issues": "https://github.com/aVadim483/fast-excel-reader/issues", - "source": "https://github.com/aVadim483/fast-excel-reader/tree/v2.23.0" + "source": "https://github.com/aVadim483/fast-excel-reader/tree/v2.24.0" }, - "time": "2025-02-06T13:48:12+00:00" + "time": "2025-02-27T13:29:39+00:00" }, { "name": "badfarm/zanzara", @@ -3585,16 +3585,16 @@ }, { "name": "symfony/cache", - "version": "v7.2.3", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "8d773a575e446de220dca03d600b2d8e1c1c10ec" + "reference": "d33cd9e14326e14a4145c21e600602eaf17cc9e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/8d773a575e446de220dca03d600b2d8e1c1c10ec", - "reference": "8d773a575e446de220dca03d600b2d8e1c1c10ec", + "url": "https://api.github.com/repos/symfony/cache/zipball/d33cd9e14326e14a4145c21e600602eaf17cc9e7", + "reference": "d33cd9e14326e14a4145c21e600602eaf17cc9e7", "shasum": "" }, "require": { @@ -3663,7 +3663,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.2.3" + "source": "https://github.com/symfony/cache/tree/v7.2.4" }, "funding": [ { @@ -3679,7 +3679,7 @@ "type": "tidelift" } ], - "time": "2025-01-27T11:08:17+00:00" + "time": "2025-02-26T09:57:54+00:00" }, { "name": "symfony/cache-contracts", @@ -3834,16 +3834,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v7.2.3", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc" + "reference": "f0a1614cccb4b8431a97076f9debc08ddca321ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc", - "reference": "1d321c4bc3fe926fd4c38999a4c9af4f5d61ddfc", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f0a1614cccb4b8431a97076f9debc08ddca321ca", + "reference": "f0a1614cccb4b8431a97076f9debc08ddca321ca", "shasum": "" }, "require": { @@ -3894,7 +3894,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.2.3" + "source": "https://github.com/symfony/dependency-injection/tree/v7.2.4" }, "funding": [ { @@ -3910,7 +3910,7 @@ "type": "tidelift" } ], - "time": "2025-01-17T10:56:55+00:00" + "time": "2025-02-21T09:47:16+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3981,16 +3981,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.2.3", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "959a74d044a6db21f4caa6d695648dcb5584cb49" + "reference": "aabf79938aa795350c07ce6464dd1985607d95d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/959a74d044a6db21f4caa6d695648dcb5584cb49", - "reference": "959a74d044a6db21f4caa6d695648dcb5584cb49", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/aabf79938aa795350c07ce6464dd1985607d95d5", + "reference": "aabf79938aa795350c07ce6464dd1985607d95d5", "shasum": "" }, "require": { @@ -4036,7 +4036,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.2.3" + "source": "https://github.com/symfony/error-handler/tree/v7.2.4" }, "funding": [ { @@ -4052,7 +4052,7 @@ "type": "tidelift" } ], - "time": "2025-01-07T09:39:55+00:00" + "time": "2025-02-02T20:27:07+00:00" }, { "name": "symfony/event-dispatcher", @@ -4342,16 +4342,16 @@ }, { "name": "symfony/framework-bundle", - "version": "v7.2.3", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "d37a43dd0b2079605fcab3056dac71934f06dc0f" + "reference": "6d6614378cd8128eed0a037ce6ac51a26c5aaed5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/d37a43dd0b2079605fcab3056dac71934f06dc0f", - "reference": "d37a43dd0b2079605fcab3056dac71934f06dc0f", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/6d6614378cd8128eed0a037ce6ac51a26c5aaed5", + "reference": "6d6614378cd8128eed0a037ce6ac51a26c5aaed5", "shasum": "" }, "require": { @@ -4472,7 +4472,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.2.3" + "source": "https://github.com/symfony/framework-bundle/tree/v7.2.4" }, "funding": [ { @@ -4488,7 +4488,7 @@ "type": "tidelift" } ], - "time": "2025-01-29T07:13:55+00:00" + "time": "2025-02-26T08:19:39+00:00" }, { "name": "symfony/http-foundation", @@ -4570,16 +4570,16 @@ }, { "name": "symfony/http-kernel", - "version": "v7.2.3", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b" + "reference": "9f1103734c5789798fefb90e91de4586039003ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b", - "reference": "caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9f1103734c5789798fefb90e91de4586039003ed", + "reference": "9f1103734c5789798fefb90e91de4586039003ed", "shasum": "" }, "require": { @@ -4664,7 +4664,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.2.3" + "source": "https://github.com/symfony/http-kernel/tree/v7.2.4" }, "funding": [ { @@ -4680,7 +4680,7 @@ "type": "tidelift" } ], - "time": "2025-01-29T07:40:13+00:00" + "time": "2025-02-26T11:01:22+00:00" }, { "name": "symfony/intl", @@ -4927,82 +4927,6 @@ ], "time": "2024-09-09T11:45:10+00:00" }, - { - "name": "symfony/polyfill-php81", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, { "name": "symfony/polyfill-php83", "version": "v1.31.0", @@ -5323,16 +5247,16 @@ }, { "name": "symfony/twig-bridge", - "version": "v7.2.2", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "29e4c66de9618e67dc1f5f13bc667aca2a228f1e" + "reference": "45c00afd4c9accf00a91215067c2858e5a9a3c4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/29e4c66de9618e67dc1f5f13bc667aca2a228f1e", - "reference": "29e4c66de9618e67dc1f5f13bc667aca2a228f1e", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/45c00afd4c9accf00a91215067c2858e5a9a3c4e", + "reference": "45c00afd4c9accf00a91215067c2858e5a9a3c4e", "shasum": "" }, "require": { @@ -5413,7 +5337,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.2.2" + "source": "https://github.com/symfony/twig-bridge/tree/v7.2.4" }, "funding": [ { @@ -5429,7 +5353,7 @@ "type": "tidelift" } ], - "time": "2024-12-19T14:25:03+00:00" + "time": "2025-02-14T14:27:24+00:00" }, { "name": "symfony/twig-bundle", @@ -5600,16 +5524,16 @@ }, { "name": "symfony/var-exporter", - "version": "v7.2.0", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "1a6a89f95a46af0f142874c9d650a6358d13070d" + "reference": "4ede73aa7a73d81506002d2caadbbdad1ef5b69a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/1a6a89f95a46af0f142874c9d650a6358d13070d", - "reference": "1a6a89f95a46af0f142874c9d650a6358d13070d", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/4ede73aa7a73d81506002d2caadbbdad1ef5b69a", + "reference": "4ede73aa7a73d81506002d2caadbbdad1ef5b69a", "shasum": "" }, "require": { @@ -5656,7 +5580,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.2.0" + "source": "https://github.com/symfony/var-exporter/tree/v7.2.4" }, "funding": [ { @@ -5672,7 +5596,7 @@ "type": "tidelift" } ], - "time": "2024-10-18T07:58:17+00:00" + "time": "2025-02-13T10:27:23+00:00" }, { "name": "thecodingmachine/safe", @@ -6006,20 +5930,20 @@ }, { "name": "twig/extra-bundle", - "version": "v3.19.0", + "version": "v3.20.0", "source": { "type": "git", "url": "https://github.com/twigphp/twig-extra-bundle.git", - "reference": "9746573ca4bc1cd03a767a183faadaf84e0c31fa" + "reference": "9df5e1dbb6a68c0665ae5603f6f2c20815647876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/9746573ca4bc1cd03a767a183faadaf84e0c31fa", - "reference": "9746573ca4bc1cd03a767a183faadaf84e0c31fa", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/9df5e1dbb6a68c0665ae5603f6f2c20815647876", + "reference": "9df5e1dbb6a68c0665ae5603f6f2c20815647876", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1.0", "symfony/framework-bundle": "^5.4|^6.4|^7.0", "symfony/twig-bundle": "^5.4|^6.4|^7.0", "twig/twig": "^3.2|^4.0" @@ -6064,7 +5988,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.19.0" + "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.20.0" }, "funding": [ { @@ -6076,24 +6000,24 @@ "type": "tidelift" } ], - "time": "2024-09-26T19:22:23+00:00" + "time": "2025-02-08T09:47:15+00:00" }, { "name": "twig/intl-extra", - "version": "v3.19.0", + "version": "v3.20.0", "source": { "type": "git", "url": "https://github.com/twigphp/intl-extra.git", - "reference": "79a1bea7254783b540d51de10dc5e9f310110794" + "reference": "05bc5d46b9df9e62399eae53e7c0b0633298b146" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/79a1bea7254783b540d51de10dc5e9f310110794", - "reference": "79a1bea7254783b540d51de10dc5e9f310110794", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/05bc5d46b9df9e62399eae53e7c0b0633298b146", + "reference": "05bc5d46b9df9e62399eae53e7c0b0633298b146", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1.0", "symfony/intl": "^5.4|^6.4|^7.0", "twig/twig": "^3.13|^4.0" }, @@ -6128,7 +6052,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/intl-extra/tree/v3.19.0" + "source": "https://github.com/twigphp/intl-extra/tree/v3.20.0" }, "funding": [ { @@ -6140,28 +6064,27 @@ "type": "tidelift" } ], - "time": "2025-01-24T20:20:33+00:00" + "time": "2025-01-31T20:45:36+00:00" }, { "name": "twig/twig", - "version": "v3.19.0", + "version": "v3.20.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e" + "reference": "3468920399451a384bef53cf7996965f7cd40183" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/d4f8c2b86374f08efc859323dbcd95c590f7124e", - "reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183", + "reference": "3468920399451a384bef53cf7996965f7cd40183", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php81": "^1.29" + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { "phpstan/phpstan": "^2.0", @@ -6208,7 +6131,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.19.0" + "source": "https://github.com/twigphp/Twig/tree/v3.20.0" }, "funding": [ { @@ -6220,7 +6143,7 @@ "type": "tidelift" } ], - "time": "2025-01-29T07:06:14+00:00" + "time": "2025-02-13T08:34:43+00:00" }, { "name": "wyrihaximus/composer-update-bin-autoload-path", @@ -6808,7 +6731,8 @@ "prefer-lowest": false, "platform": { "php": "^8.4", - "ext-gd": "^8.4" + "ext-gd": "^8.4", + "ext-intl": "^8.4" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/examples/arangodb/collections/menu/index.json b/examples/arangodb/collections/menu/index.json index 1b46b98..3173e77 100644 --- a/examples/arangodb/collections/menu/index.json +++ b/examples/arangodb/collections/menu/index.json @@ -9,10 +9,8 @@ }, "class": {}, "icon": { - "style": { - "rotate": "-135deg" - }, - "class": "arrow circle" + "style": {}, + "class": "house" }, "image": { "storage": null diff --git a/mirzaev/huesos/system/models/telegram.php b/mirzaev/huesos/system/models/telegram.php index 126b47f..5f7ab25 100755 --- a/mirzaev/huesos/system/models/telegram.php +++ b/mirzaev/huesos/system/models/telegram.php @@ -674,13 +674,13 @@ final class telegram extends core $symbol = ($account->currency ?? currency::rub)->symbol(); // Initializing delivery cost for message - $delivery_cost = $cart->buffer['delivery']['cost']; + $delivery_cost = static::unmarkdown((string) $cart->buffer['delivery']['cost']); // Initializing delivery days for message - $delivery_days = $cart->buffer['delivery']['days']; + $delivery_days = static::unmarkdown((string) $cart->buffer['delivery']['days']); // Initializing delivery address for message - $delivery_address = $cart->buffer['delivery']['location']['name'] . ', ' . $cart->buffer['delivery']['street']; + $delivery_address = static::unmarkdown($cart->buffer['delivery']['location']['name'] . ', ' . $cart->buffer['delivery']['street']); $ctx->sendMessage( << { // Not imported the damper module // Execute under damper - this.search.system(force); + catalog.search.system(); }, ); } @@ -125,13 +125,13 @@ export default class catalog { // Imported the damper module // Execute under damper - this.search.damper(force); + catalog.search.damper(force); }, () => { // Not imported the damper module // Execute under damper - this.search.system(force); + catalog.search.system(); }, ); } @@ -144,6 +144,36 @@ export default class catalog { return false; } + /** + * @name Back (interface) + * + * @description + * Request to go back by the catalog + * + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Did the execution complete without errors? + */ + static back(force = false) { + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + catalog.back.damper(force); + }, + () => { + // Not imported the damper module + + // Execute + catalog.back.system(); + }, + ); + + // Exit (success) + return true; + } + /** * @name Product card (interface) * @@ -166,13 +196,13 @@ export default class catalog { // Imported the damper module // Execute under damper - this.product.damper(identifier, force); + catalog.product.damper(identifier, force); }, () => { // Not imported the damper module // Execute - this.product.system(identifier, force); + catalog.product.system(identifier); }, ); @@ -188,7 +218,7 @@ core.modules.connect("damper").then(() => { catalog.search, { /** - * @name Search (system) + * @name Search (damper) * * @description * Request search in the catalog and render result in the core.main @@ -202,7 +232,30 @@ core.modules.connect("damper").then(() => { damper: core.damper( (...variables) => catalog.search.system(...variables), 300, - 1, + 0, + ), + }, + ); + + Object.assign( + catalog.back, + { + /** + * @name Back (damper) + * + * @description + * Request to going back by the catalog + * + * @memberof catalog.back + * + * @param {bool} force Ignore the damper? (false) + * + * @return {void} + */ + damper: core.damper( + (...variables) => catalog.back.system(...variables), + 300, + 0, ), }, ); @@ -232,554 +285,6 @@ core.modules.connect("damper").then(() => { ); }); -Object.assign( - catalog.product, - { - /** - * @name Product card (system) - * - * @description - * Request product data and render result in the core.window - * - * @memberof catalog.product - * - * @param {string} identifier Identifier of the product - * - * @return {Promise} Request to the server - */ - async system( - identifier, - resolve = () => {}, - reject = () => {}, - ) { - try { - if (typeof identifier === "string") { - // Validated identifier - - // Initialize the buffer of URN parameters @todo after opening window add to document.location.search - const parameters = new URLSearchParams( - document.location.search, - ); - - // Write parameter to the buffer of URN parameters - parameters.set("product", identifier); - - // Intializing URI of the request - const uri = "?" + parameters; - - return await core.request(uri, undefined, "GET").then( - async (json) => { - if (json) { - // Received a JSON-response - - if ( - json.errors !== null && - typeof json.errors === "object" && - json.errors.length > 0 - ) { - // Fail (received errors) - - // Exit (fail) - reject(json); - } else { - // Success (not received errors) - - if (core.window instanceof HTMLElement) { - // Initialized active window - - // Delete active window - core.window.remove(); - } - - core.modules.connect(["cart", "telegram"]).then(async () => { - // Imported the telegram module - - if ( - json.product !== null && - typeof json.product === "object" - ) { - // Received data of the product - - // Writing to the browser history - history.pushState({}, json.product.name, uri); - - // Deinitializing of the old winow - const old = document.getElementById("window"); - if (old instanceof HTMLElement) old.remove(); - - const wrap = document.createElement("section"); - wrap.setAttribute("id", "window"); - - const card = document.createElement("div"); - // card.classList.add("product", "card"); - card.classList.add("card"); - - const h3 = document.createElement("h3"); - - const name = document.createElement("span"); - name.classList.add("name", "unselectable"); - name.setAttribute("title", identifier); - name.innerText = json.product.name; - - const exit = document.createElement("a"); - exit.classList.add("exit"); - exit.setAttribute("type", "button"); - - const exit_icon = document.createElement("i"); - exit_icon.classList.add("icon", "close"); - - const images = document.createElement("div"); - images.setAttribute("id", "images"); - images.classList.add("images", "unselectable"); - - for ( - const [index, uri] of json.product.images["200"] - .entries() - ) { - const image = document.createElement("img"); - image.setAttribute("src", uri); - image.setAttribute("data-image-small", uri); - image.setAttribute( - "data-image-big", - json.product.images["800"][index], - ); - image.setAttribute("ondragstart", "return false;"); - - images.appendChild(image); - } - - let width = 0; - let buffer; - [...images.children].forEach( - // 150px instead of child.offsetWidth by min-width in window.css - (child) => (width += 150 + - (isNaN( - buffer = parseFloat( - getComputedStyle(child).marginRight, - ), - ) - ? 0 - : buffer)), - ); - - const button = core.telegram.api.isVisibless; - - // блокировка закрытия изображений - let images_from; - const _images_from = (event) => (images_from = event); - images.addEventListener("mousedown", _images_from); - images.addEventListener("touchstart", _images_from); - - let touches; - images.addEventListener('touchmove', (move) => { - touches = move.touches; - }); - - const _open = (event) => { - if ( - event.type === "touchstart" || - event.button === 0 - ) { - const x = event.pageX || - (event.touches && event.touches[0]?.pageX) || 0; - const y = event.pageY || - (event.touches && event.touches[0]?.pageY) || 0; - const _x = images_from.pageX || - (images_from.touches && - images_from.touches[0]?.pageX) || - 0; - const _y = images_from.pageY || - (images_from.touches && - images_from.touches[0]?.pageY) || - 0; - - if ( - _x - x < 10 && - _x - x > -10 && - _y - y < 10 && - _y - y > -10 - ) { - // Replacing small images with big images - Array.from(images.children).forEach((image) => - image.setAttribute( - "src", - image.getAttribute("data-image-big"), - ) - ); - - // Expanding the "Web App" window - core.telegram.api.expand(); - core.telegram.api.disableVerticalSwipes(); - - images.classList.add("extend"); - - if (button) { - core.telegram.api.MainButton.hide(); - } - - setTimeout(() => { - images.hotline.alive = false; - images.hotline.movable = false; - images.hotline.restart(); - - images.addEventListener("mouseup", _close); - images.addEventListener("touchend", _close); - }, 300); - images.removeEventListener("mouseup", _open); - images.removeEventListener("touchend", _open); - } - } - }; - - const _close = (event) => { - console.log(event) - const x = event.pageX || - (touches && touches[0]?.pageX) || 0; - const y = event.pageY || - (touches && touches[0]?.pageY) || 0; - const _x = images_from.pageX || - (images_from.touches && - images_from.touches[0]?.pageX) || - 0; - const _y = images_from.pageY || - (images_from.touches && - images_from.touches[0]?.pageY) || - 0; - - if ( - event.type === "touchend" || - event.button === 0 - ) { - if ( - _x - x < 10 && - _x - x > -10 && - _y - y < 10 && - _y - y > -10 - ) { - // Курсор не был сдвинут на 10 квардратных пикселей - // (в идеале надо переделать на таймер 2 секунды есди зажата кнопка то ничего не делать а просто двигать) - // пох абсолютно сейчас заказчик слишком охуевший для этого - - images.hotline.movable = true; - images.hotline.alive = true; - images.hotline.step = -1; - images.hotline.wheel = true; - images.hotline.restart(); - - if (width < card.offsetWidth) { - images.hotline.stop(); - } - - // Replacing big images with small images - Array.from(images.children).forEach((image) => - image.setAttribute( - "src", - image.getAttribute("data-image-small"), - ) - ); - - images.classList.remove("extend"); - - - core.telegram.api.enableVerticalSwipes(); - - if (button) { - core.telegram.api.MainButton.show(); - } - - images.removeEventListener("mouseup", _close); - images.removeEventListener("touchend", _close); - images.addEventListener("mousedown", _start); - images.addEventListener("touchstart", _start); - } else { - // Курсор был сдвинут более чем на 10 пикселей - - if (x > _x) images.hotline.forward(); - else images.hotline.backward(); - } - } - }; - - const _start = (event) => { - if ( - event.type === "touchstart" || - event.button === 0 - ) { - images.removeEventListener("mousedown", _start); - images.removeEventListener("touchstart", _start); - images.addEventListener("mouseup", _open); - images.addEventListener("touchend", _open); - } - }; - - images.addEventListener("mousedown", _start); - images.addEventListener("touchstart", _start); - - const header = document.createElement("p"); - header.classList.add("header"); - - const brand = document.createElement("small"); - brand.classList.add("brand"); - brand.innerText = json.product.brand; - - const description = document.createElement("p"); - description.classList.add("description"); - description.innerText = json.product.description; - - const compatibility = document.createElement("p"); - compatibility.classList.add("compatibility"); - compatibility.innerText = json.product.compatibility; - - const footer = document.createElement("div"); - footer.classList.add("footer"); - footer.classList.add("footer"); - - const dimensions = document.createElement("small"); - dimensions.classList.add("dimensions"); - - const x = json.product.dimensions.x; - const y = json.product.dimensions.y; - const z = json.product.dimensions.z; - - let formatted = ""; - - if (x !== "") formatted = x; - if (y !== "") { - if (formatted.length === 0) formatted = y; - else formatted += "x" + y; - } - if (z !== "") { - if (formatted.length === 0) formatted = z; - else formatted += "x" + z; - } - - dimensions.innerText = formatted; - - const weight = document.createElement("small"); - weight.classList.add("weight"); - weight.innerText = json.product.weight + "г"; - - const cost = document.createElement("p"); - cost.classList.add("cost", "currency"); - cost.innerText = json.product.cost; - - h3.appendChild(name); - exit.appendChild(exit_icon); - h3.appendChild(exit); - card.appendChild(h3); - card.appendChild(images); - header.appendChild(brand); - card.appendChild(header); - card.appendChild(description); - card.appendChild(compatibility); - footer.appendChild(dimensions); - footer.appendChild(weight); - footer.appendChild(cost); - card.appendChild(footer); - wrap.appendChild(card); - document.body.appendChild(wrap); - - // Reinitialize parameter - core.window = document.getElementById("window"); - - // Write - const add = () => { - core.cart.write( - undefined, - document.getElementById( - "product_" + json.product.identifier, - ), - ); - - core.telegram.api.MainButton - .setText(json.product.cart.text.added) - .setParams({ - color: "#90be36", - has_shine_effect: true, - }) - .offClick(add) - .onClick(added); - }; - - // Delete - const added = () => { - core.cart.set( - undefined, - document.getElementById( - "product_" + json.product.identifier, - ), - 0, - ); - - core.telegram.api.MainButton - .setText(json.product.cart.text.add) - .setParams({ - color: core.telegram.api.themeParams.button_color, - // has_shine_effect: json.product.discount > 0, - has_shine_effect: false, - }) - .offClick(added) - .onClick(add); - }; - - if (json.product.cart.amount > 0) { - // Initializing the "Main Button" of the "Web App" window - core.telegram.api.MainButton - .setText(json.product.cart.text.added) - .setParams({ - color: "#90be36", - has_shine_effect: true, - }) - .onClick(added) - .hideProgress() - .enable() - .show(); - } else { - // Initializing the "Main Button" of the "Web App" window - core.telegram.api.MainButton - .setText(json.product.cart.text.add) - .setParams({ - color: core.telegram.api.themeParams.button_color, - // has_shine_effect: json.product.discount > 0, - has_shine_effect: false, - }) - .onClick(add) - .hideProgress() - .enable() - .show(); - } - - // блокировка закрытия карточки - let from; - const _from = (event) => (from = event.target); - wrap.addEventListener("mousedown", _from); - wrap.addEventListener("touchstart", _from); - - const remove = (event) => { - if ( - typeof event === "undefined" || - event.type !== "popstate" - ) { - history.back(); - - // Initialize the buffer of URN parameters @todo after opening window add to document.location.search - const parameters = new URLSearchParams( - document.location.search, - ); - - if (parameters.get("product") === identifier) { - // The previous window with the product card was exactly the same - - // Deleting product card - parameters.delete("product"); - - // Writing to the browser history - history.pushState( - {}, - json.product.name, - "?" + parameters, - ); - } - - // Write parameter to the buffer of URN parameters - parameters.set("product", identifier); - } - - wrap.remove(); - images.removeEventListener( - "mousedown", - _images_from, - ); - images.removeEventListener( - "touchstart", - _images_from, - ); - wrap.removeEventListener("mousedown", _from); - wrap.removeEventListener("touchstart", _from); - exit.removeEventListener("click", remove); - exit.removeEventListener("touch", remove); - document.removeEventListener("click", close); - document.removeEventListener("touch", close); - window.removeEventListener("popstate", remove); - - // Denitializing the "Back Button" of the "Web App" window - core.telegram.api.BackButton.hide(); - core.telegram.api.BackButton.offClick(remove); - - // Deinitializing the "Main Button" of the "Web App" window - core.telegram.api.MainButton - .offClick(add) - .offClick(added) - .disable() - .hide(); - }; - - const close = (event) => { - if ( - from === wrap && - !card.contains(event.target) && - !!card && - !!( - card.offsetWidth || - card.offsetHeight || - card.getClientRects().length - ) - ) { - remove(); - } - - from = undefined; - }; - - exit.addEventListener("click", remove); - exit.addEventListener("touch", remove); - document.addEventListener("click", close); - document.addEventListener("touch", close); - window.addEventListener("popstate", remove); - - // Initializing the "Back Button" of the "Web App" window - core.telegram.api.BackButton.show(); - core.telegram.api.BackButton.onClick(remove); - - // setTimeout(() => { - import("./hotline.mjs").then((hotline) => { - // Imported the hotline module - images.hotline = new hotline.default( - images, - ); - images.hotline.step = -1; - images.hotline.interval = 20; - images.hotline.wheel = true; - images.hotline.touch = true; - images.hotline.events.set("transfer.beginning", true); - images.hotline.events.set("transfer.end", true); - images.hotline.events.set("moved.forward", true); - images.hotline.events.set("moved.backward", true); - - if (width > card.offsetWidth) { - images.hotline.start(); - } - }); - // }, 300); - } - }); - - // Exit (success) - resolve(); - } - } - }, - () => reject(), - ); - } - } catch (e) { - // Exit (fail) - reject(e); - } - }, - }, -); - Object.assign( catalog.search, { @@ -830,6 +335,14 @@ Object.assign( if (json) { // Received a JSON-response + // Initializing the BackButton of the "Web App" window + core.telegram.api.BackButton.show(); + + // Initializing the BackButton event listener + core.modules.connect(["telegram"]).then(() => + core.telegram.api.BackButton.onClick(core.catalog.back) + ); + // Initializing the search element const input = document.querySelector("search#search>input"); @@ -1414,5 +927,1173 @@ Object.assign( }, ); +Object.assign( + catalog.back, + { + /** + * @name Back (system) + * + * @description + * Request to go back by the catalog + * + * @memberof catalog.back + * + * @return {Promise} Request to the server + */ + async system( + resolve = () => {}, + reject = () => {}, + ) { + // Initializing the previous URI + const previous = history.state?.previous || "/"; + + if (typeof previous === "string" && previous.length > 0) { + // Initialized the previous URI + + core.loader.load(previous, null, true) + .then( + (json) => { + if (json) { + // Received a JSON-response + + if (typeof history.state?.previous === "undefined") { + // Not initialized the previous URI + + // Deinitializing the "Back Button" of the "Web App" window + core.telegram.api.BackButton.hide(); + } + + // Initializing the search element + const input = document.querySelector("search#search>input"); + + if (input instanceof HTMLElement) { + // Found the search element + + input.removeAttribute("disabled"); + // input.focus(); + } + + /** + * Title

+ */ + if ( + typeof json.title === "string" && + json.title.length > 0 + ) { + // Received and validated text of the title

+ + // Initializing the title

element + const title = document.getElementById("title"); + + if (title instanceof HTMLElement) { + // Found the title

element + + // Writing into the title

element + title.innerText = json.title; + } else { + // Not found the title

element + + // Initialize the title

element + const title = document.createElement("h2"); + + if (core.main instanceof HTMLElement) { + // Found the
element + + // Inititalize the first element in the
element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the
element + + // Writing the title

element before the first element in the
element + core.main.insertBefore( + title, + first, + ); + } + } + + // Writing into the title

element + title.innerText = json.title; + } + } else { + // Not received or not validated text of the title

+ + // Deleting the title

element + document.getElementById("title")?.remove(); + } + + /** + * Search search + */ + if ( + typeof json.search === "string" && + json.search.length > 0 + ) { + // Received and validated the searcn HTML-code + + // Initializing the search element + const search = document.getElementById("search"); + + if (search instanceof HTMLElement) { + // Found the search element + + // Writing into the search element + search.outerHTML = json.search; + } else { + // Not found the search element + + // Initialize the search element + const search = document.createElement("search"); + + if (core.main instanceof HTMLElement) { + // Found the
element + + // Initializing the title

element + const title = document.getElementById("title"); + + if (title instanceof HTMLElement) { + // Initialized the title

elemment in the
element + + // Writing the search element after the title

element in the
element + core.main.insertBefore( + search, + title.nextElementSibling, + ); + } else { + // Not initialized the title

element in the
element + + // Inititalize the first element in the
element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the
element + + // Writing the search element before the first element in the
element + core.main.insertBefore( + search, + first, + ); + } + } + } + + // Writing into the search element + search.outerHTML = json.search; + } + } else { + // Not received or not validated the search HTML-code + + if ( + typeof json.search !== undefined && json.search === "" + ) { + // Requested to delete the search element + + // Deleting the search element + document.getElementById("search")?.remove(); + } + } + + /** + * Categories + */ + if ( + typeof json.categories === "string" && + json.categories.length > 0 + ) { + // Received and validated the categories HTML-code + + // Initializing the categories
element + const categories = document.getElementById("categories"); + + if (categories instanceof HTMLElement) { + // Found the categories
element + + // Writing into the categories
element + categories.outerHTML = json.categories; + } else { + // Not found the categories
element + + // Initialize the categories
element + const categories = document.createElement("section"); + + if (core.main instanceof HTMLElement) { + // Found the
element + + // Initializing the search element + const search = document.getElementById("search"); + + if (search instanceof HTMLElement) { + // Initialized the search element in the
element + + // Writing the categories
element after the search element in the
element + core.main.insertBefore( + categories, + search.nextElementSibling, + ); + } else { + // Not initialized the search element in the
element + + // Initializing the title

element + const title = document.getElementById("title"); + + if ( + title instanceof HTMLElement + ) { + // Initialized the title

element in the
element + + // Writing the categories
element after the title

element in the
element + core.main.insertBefore( + categories, + title.nextElementSibling, + ); + } else { + // Not initialized the title

element in the
element + + // Inititalize the first element in the
element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the
element + + // Writing the categories
before the first element in the
element + core.main.insertBefore( + categories, + first, + ); + } + } + } + } + + // Writing into the categories
element + categories.outerHTML = json.categories; + } + } else { + // Not received or not validated the categories HTML-code + + // Deleting the categories
element + document.getElementById("categories")?.remove(); + } + + /** + * Filters + */ + if ( + typeof json.filters === "string" && + json.filters.length > 0 + ) { + // Received and validated the filters HTML-code + + // Initializing the filters
element + const filters = document.getElementById("filters"); + + if (filters instanceof HTMLElement) { + // Found the filters
element + + // Writing into the filters
element + filters.outerHTML = json.filters; + } else { + // Not found the filters
element + + // Initialize the filters
element + const filters = document.createElement("section"); + + if (core.main instanceof HTMLElement) { + // Found the
element + + // Initializing the categories
element + const categories = document.getElementById( + "categories", + ); + + if ( + categories instanceof HTMLElement + ) { + // Initialized the categories
element in the
element + + // Writing the filters
element after the categories
element in the
element + core.main.insertBefore( + filters, + categories.nextElementSibling, + ); + } else { + // Not initialized the categories
element in the
element + + // Initializing the search element + const search = document.getElementById("search"); + + if ( + search instanceof HTMLElement + ) { + // Initialized the search element in the
element + + // Writing the filters
element after the search element in the
element + core.main.insertBefore( + filters, + search.nextElementSibling, + ); + } else { + // Not initialized the search element in the
element + + // Initializing the title

element + const title = document.getElementById("title"); + + if ( + title instanceof HTMLElement + ) { + // Initialized the title

element in the
element + + // Writing the filters
element after the title

element in the
element + core.main.insertBefore( + filters, + title.nextElementSibling, + ); + } else { + // Not initialized the title

element in the
element + + // Inititalize the first element in the
element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the
element + + // Writing the filters
before the first element in the
element + core.main.insertBefore( + filters, + first, + ); + } + } + } + } + } + + // Writing into the filters
element + filters.outerHTML = json.filters; + } + } else { + // Not received or not validated the filters HTML-code + + // Deleting the filters
element + document.getElementById("filters")?.remove(); + } + + /** + * Sorting + */ + if ( + typeof json.sorting === "string" && + json.sorting.length > 0 + ) { + // Received and validated the sorting HTML-code + + // Initializing the sorting
element + const sorting = document.getElementById("sorting"); + + if (sorting instanceof HTMLElement) { + // Found the sorting
element + + // Writing into the sorting
element + sorting.outerHTML = json.sorting; + } else { + // Not found the sorting
element + + // Initialize the sorting
element + sorting = document.createElement("section"); + + if (core.main instanceof HTMLElement) { + // Found the
element + + // Initializing the filters
element + const filters = document.getElementById("filters"); + + if (filters instanceof HTMLElement) { + // Initialized the filters
element in the
element + + // Writing the sorting
element after the filters
element in the
element + core.main.insertBefore( + sorting, + filters.nextElementSibling, + ); + } else { + // Not initialized the filters
element in the
element + + // Initializing the categories
element + const categories = document.getElementById( + "categories", + ); + + if ( + categories instanceof HTMLElement + ) { + // Initialized the categories
element in the
element + + // Writing the sorting
element after the categories
element in the
element + core.main.insertBefore( + sorting, + categories.nextElementSibling, + ); + } else { + // Notnitialized the categories
element in the
element + + // Initializing the search element + const search = document.getElementById("search"); + + if ( + search instanceof HTMLElement + ) { + // Initialized the search element in the
element + + // Writing the sorting
element after the search element in the
element + core.main.insertBefore( + sorting, + search.nextElementSibling, + ); + } else { + // Not nitialized the search element in the
element + + // Initializing the title

element + const title = document.getElementById("title"); + + if ( + title instanceof HTMLElement + ) { + // Initialized the title

element in the
element + + // Writing the sorting
element after the title

element in the
element + core.main.insertBefore( + sorting, + title.nextElementSibling, + ); + } else { + // Not initialized the title

element in the
element + + // Inititalize the first element in the
element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the
element + + // Writing the sorting
before the first element in the
element + core.main.insertBefore( + sorting, + first, + ); + } + } + } + } + } + } + + // Writing into the sorting
element + sorting.outerHTML = json.sorting; + } + } else { + // Not received or not validated the sorting HTML-code + + // Deleting the sorting
element + document.getElementById("sorting")?.remove(); + } + + /** + * Products + */ + if ( + typeof json.products === "string" && + json.products.length > 0 + ) { + // Received and validated the products HTML-code + + // Initializing the products
element + const products = document.getElementById("products"); + + if (products instanceof HTMLElement) { + // Found the products
element + + // Writing into the products
element + products.outerHTML = json.products; + } else { + // Not found the products
element element + + // Initialize the products
element + const products = document.createElement("section"); + + if (core.main instanceof HTMLElement) { + // Found the
element + + // Initializing the sorting
element + const sorting = document.getElementById("sorting"); + + if (sorting instanceof HTMLElement) { + // Initialized the sorting
element in the
element + + // Writing the products
element after the sorting
element in the
element + core.main.insertBefore( + products, + sorting.nextElementSibling, + ); + } else { + // Initialized the sorting
element in the
element + + // Initializing the filters
element + const filters = document.getElementById("filters"); + + if ( + filters instanceof HTMLElement + ) { + // Initialized the filters
element in the
element + + // Writing the products
element after the filters
element in the
element + core.main.insertBefore( + products, + filters.nextElementSibling, + ); + } else { + // Not initialized the filters
element in the
element + + // Initializing the categories
element + const categories = document.getElementById( + "categories", + ); + + if ( + categories instanceof HTMLElement + ) { + // Initialized the categories
element in the
element + + // Writing the products
element after the categories
element in the
element + core.main.insertBefore( + products, + categories.nextElementSibling, + ); + } else { + // Not initialized the categories
element in the
element + + // Initializing the search element + const search = document.getElementById("search"); + + if ( + search instanceof HTMLElement + ) { + // Initialized the search element in the
element + + // Writing the products
element after the search element in the
element + core.main.insertBefore( + products, + search.nextElementSibling, + ); + } else { + // Not initialized the search element in the
element + + // Initializing the title

element + const title = document.getElementById("title"); + + if ( + title instanceof HTMLElement + ) { + // Initialized the title

element in the
element + + // Writing the products
element after the title

element in the
element + core.main.insertBefore( + products, + title.nextElementSibling, + ); + } else { + // Not initialized the title

element in the
element + + // Inititalize the first element in the
element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the
element + + // Writing the products
before the first element in the
element + core.main.insertBefore( + products, + first, + ); + } + } + } + } + } + } + } + + // Writing into the products
element + products.outerHTML = json.products; + } + } else { + // Not received or not validated the products HTML-code + + // Deleting the products
element + document.getElementById("products")?.remove(); + } + } + + // Exit (success) + resolve(); + }, + ); + } else { + // Not initialized the previous URI + + // Deinitializing the "Back Button" of the "Web App" window + core.telegram.api.BackButton.hide(); + } + }, + }, +); + +Object.assign( + catalog.product, + { + /** + * @name Product card (system) + * + * @description + * Request product data and render result in the core.window + * + * @memberof catalog.product + * + * @param {string} identifier Identifier of the product + * + * @return {Promise} Request to the server + */ + async system( + identifier, + resolve = () => {}, + reject = () => {}, + ) { + try { + if (typeof identifier === "string") { + // Validated identifier + + // Initialize the buffer of URN parameters @todo after opening window add to document.location.search + const parameters = new URLSearchParams( + document.location.search, + ); + + // Write parameter to the buffer of URN parameters + parameters.set("product", identifier); + + // Intializing URI of the request + const uri = "?" + parameters; + + return await core.request(uri, undefined, "GET").then( + async (json) => { + if (json) { + // Received a JSON-response + + if ( + json.errors !== null && + typeof json.errors === "object" && + json.errors.length > 0 + ) { + // Fail (received errors) + + // Exit (fail) + reject(json); + } else { + // Success (not received errors) + + if (core.window instanceof HTMLElement) { + // Initialized active window + + // Delete active window + core.window.remove(); + } + + core.modules.connect(["cart", "telegram"]).then(async () => { + // Imported the telegram module + + if ( + json.product !== null && + typeof json.product === "object" + ) { + // Received data of the product + + // Writing to the browser history + history.pushState({}, json.product.name, uri); + + // Deinitializing of the old winow + const old = document.getElementById("window"); + if (old instanceof HTMLElement) old.remove(); + + const wrap = document.createElement("section"); + wrap.setAttribute("id", "window"); + + const card = document.createElement("div"); + // card.classList.add("product", "card"); + card.classList.add("card"); + + const h3 = document.createElement("h3"); + + const name = document.createElement("span"); + name.classList.add("name", "unselectable"); + name.setAttribute("title", identifier); + name.innerText = json.product.name; + + const exit = document.createElement("a"); + exit.classList.add("exit"); + exit.setAttribute("type", "button"); + + const exit_icon = document.createElement("i"); + exit_icon.classList.add("icon", "close"); + + const images = document.createElement("div"); + images.setAttribute("id", "images"); + images.classList.add("images", "unselectable"); + + for ( + const [index, uri] of json.product.images["200"] + .entries() + ) { + const image = document.createElement("img"); + image.setAttribute("src", uri); + image.setAttribute("data-image-small", uri); + image.setAttribute( + "data-image-big", + json.product.images["800"][index], + ); + image.setAttribute("ondragstart", "return false;"); + + images.appendChild(image); + } + + let width = 0; + let buffer; + [...images.children].forEach( + // 150px instead of child.offsetWidth by min-width in window.css + (child) => (width += 150 + + (isNaN( + buffer = parseFloat( + getComputedStyle(child).marginRight, + ), + ) + ? 0 + : buffer)), + ); + + const button = core.telegram.api.isVisibless; + + // блокировка закрытия изображений + let images_from; + const _images_from = (event) => (images_from = event); + images.addEventListener("mousedown", _images_from); + images.addEventListener("touchstart", _images_from); + + let touches; + images.addEventListener("touchmove", (move) => { + touches = move.touches; + }); + + const _open = (event) => { + if ( + event.type === "touchstart" || + event.button === 0 + ) { + const x = event.pageX || + (event.touches && event.touches[0]?.pageX) || 0; + const y = event.pageY || + (event.touches && event.touches[0]?.pageY) || 0; + const _x = images_from.pageX || + (images_from.touches && + images_from.touches[0]?.pageX) || + 0; + const _y = images_from.pageY || + (images_from.touches && + images_from.touches[0]?.pageY) || + 0; + + if ( + _x - x < 10 && + _x - x > -10 && + _y - y < 10 && + _y - y > -10 + ) { + // Replacing small images with big images + Array.from(images.children).forEach((image) => + image.setAttribute( + "src", + image.getAttribute("data-image-big"), + ) + ); + + // Expanding the "Web App" window + core.telegram.api.expand(); + core.telegram.api.disableVerticalSwipes(); + + images.classList.add("extend"); + + if (button) { + core.telegram.api.MainButton.hide(); + } + + setTimeout(() => { + images.hotline.alive = false; + images.hotline.movable = false; + images.hotline.restart(); + + images.addEventListener("mouseup", _close); + images.addEventListener("touchend", _close); + }, 300); + images.removeEventListener("mouseup", _open); + images.removeEventListener("touchend", _open); + } + } + }; + + const _close = (event) => { + const x = event.pageX || + (touches && touches[0]?.pageX) || 0; + const y = event.pageY || + (touches && touches[0]?.pageY) || 0; + const _x = images_from.pageX || + (images_from.touches && + images_from.touches[0]?.pageX) || + 0; + const _y = images_from.pageY || + (images_from.touches && + images_from.touches[0]?.pageY) || + 0; + + if ( + event.type === "touchend" || + event.button === 0 + ) { + if ( + _x - x < 10 && + _x - x > -10 && + _y - y < 10 && + _y - y > -10 + ) { + // Курсор не был сдвинут на 10 квардратных пикселей + // (в идеале надо переделать на таймер 2 секунды есди зажата кнопка то ничего не делать а просто двигать) + // пох абсолютно сейчас заказчик слишком охуевший для этого + + images.hotline.movable = true; + images.hotline.alive = true; + images.hotline.step = -1; + images.hotline.wheel = true; + images.hotline.restart(); + + if (width < card.offsetWidth) { + images.hotline.stop(); + } + + // Replacing big images with small images + Array.from(images.children).forEach((image) => + image.setAttribute( + "src", + image.getAttribute("data-image-small"), + ) + ); + + images.classList.remove("extend"); + + core.telegram.api.enableVerticalSwipes(); + + if (button) { + core.telegram.api.MainButton.show(); + } + + images.removeEventListener("mouseup", _close); + images.removeEventListener("touchend", _close); + images.addEventListener("mousedown", _start); + images.addEventListener("touchstart", _start); + } else { + // Курсор был сдвинут более чем на 10 пикселей + + if (x > _x) images.hotline.forward(); + else images.hotline.backward(); + } + } + }; + + const _start = (event) => { + if ( + event.type === "touchstart" || + event.button === 0 + ) { + images.removeEventListener("mousedown", _start); + images.removeEventListener("touchstart", _start); + images.addEventListener("mouseup", _open); + images.addEventListener("touchend", _open); + } + }; + + images.addEventListener("mousedown", _start); + images.addEventListener("touchstart", _start); + + const header = document.createElement("p"); + header.classList.add("header"); + + const brand = document.createElement("small"); + brand.classList.add("brand"); + brand.innerText = json.product.brand; + + const description = document.createElement("p"); + description.classList.add("description"); + description.innerText = json.product.description; + + const compatibility = document.createElement("p"); + compatibility.classList.add("compatibility"); + compatibility.innerText = json.product.compatibility; + + const footer = document.createElement("div"); + footer.classList.add("footer"); + footer.classList.add("footer"); + + const dimensions = document.createElement("small"); + dimensions.classList.add("dimensions"); + + const x = json.product.dimensions.x; + const y = json.product.dimensions.y; + const z = json.product.dimensions.z; + + let formatted = ""; + + if (x !== "") formatted = x; + if (y !== "") { + if (formatted.length === 0) formatted = y; + else formatted += "x" + y; + } + if (z !== "") { + if (formatted.length === 0) formatted = z; + else formatted += "x" + z; + } + + dimensions.innerText = formatted; + + const weight = document.createElement("small"); + weight.classList.add("weight"); + weight.innerText = json.product.weight + "г"; + + const cost = document.createElement("p"); + cost.classList.add("cost", "currency"); + cost.innerText = json.product.cost; + + h3.appendChild(name); + exit.appendChild(exit_icon); + h3.appendChild(exit); + card.appendChild(h3); + card.appendChild(images); + header.appendChild(brand); + card.appendChild(header); + card.appendChild(description); + card.appendChild(compatibility); + footer.appendChild(dimensions); + footer.appendChild(weight); + footer.appendChild(cost); + card.appendChild(footer); + wrap.appendChild(card); + document.body.appendChild(wrap); + + // Reinitialize parameter + core.window = document.getElementById("window"); + + // Write + const add = () => { + core.cart.write( + undefined, + document.getElementById( + "product_" + json.product.identifier, + ), + ); + + core.telegram.api.MainButton + .setText(json.product.cart.text.added) + .setParams({ + color: "#90be36", + has_shine_effect: true, + }) + .offClick(add) + .onClick(added); + }; + + // Delete + const added = () => { + core.cart.set( + undefined, + document.getElementById( + "product_" + json.product.identifier, + ), + 0, + ); + + core.telegram.api.MainButton + .setText(json.product.cart.text.add) + .setParams({ + color: core.telegram.api.themeParams.button_color, + // has_shine_effect: json.product.discount > 0, + has_shine_effect: false, + }) + .offClick(added) + .onClick(add); + }; + + if (json.product.cart.amount > 0) { + // Initializing the "Main Button" of the "Web App" window + core.telegram.api.MainButton + .setText(json.product.cart.text.added) + .setParams({ + color: "#90be36", + has_shine_effect: true, + }) + .onClick(added) + .hideProgress() + .enable() + .show(); + } else { + // Initializing the "Main Button" of the "Web App" window + core.telegram.api.MainButton + .setText(json.product.cart.text.add) + .setParams({ + color: core.telegram.api.themeParams.button_color, + // has_shine_effect: json.product.discount > 0, + has_shine_effect: false, + }) + .onClick(add) + .hideProgress() + .enable() + .show(); + } + + // блокировка закрытия карточки + let from; + const _from = (event) => (from = event.target); + wrap.addEventListener("mousedown", _from); + wrap.addEventListener("touchstart", _from); + + const remove = (event) => { + if ( + typeof event === "undefined" || + event.type !== "popstate" + ) { + history.back(); + + // Initialize the buffer of URN parameters @todo after opening window add to document.location.search + const parameters = new URLSearchParams( + document.location.search, + ); + + if (parameters.get("product") === identifier) { + // The previous window with the product card was exactly the same + + // Deleting product card + parameters.delete("product"); + + // Writing to the browser history + history.pushState( + {}, + json.product.name, + "?" + parameters, + ); + } + + // Write parameter to the buffer of URN parameters + parameters.set("product", identifier); + } + + wrap.remove(); + images.removeEventListener( + "mousedown", + _images_from, + ); + images.removeEventListener( + "touchstart", + _images_from, + ); + wrap.removeEventListener("mousedown", _from); + wrap.removeEventListener("touchstart", _from); + exit.removeEventListener("click", remove); + exit.removeEventListener("touch", remove); + document.removeEventListener("click", close); + document.removeEventListener("touch", close); + window.removeEventListener("popstate", remove); + + // Denitializing the "Back Button" of the "Web App" window + // core.telegram.api.BackButton.hide(); + core.telegram.api.BackButton.onClick(core.catalog.back); + core.telegram.api.BackButton.offClick(remove); + + // Deinitializing the "Main Button" of the "Web App" window + core.telegram.api.MainButton + .offClick(add) + .offClick(added) + .disable() + .hide(); + }; + + const close = (event) => { + if ( + from === wrap && + !card.contains(event.target) && + !!card && + !!( + card.offsetWidth || + card.offsetHeight || + card.getClientRects().length + ) + ) { + remove(); + } + + from = undefined; + }; + + exit.addEventListener("click", remove); + exit.addEventListener("touch", remove); + document.addEventListener("click", close); + document.addEventListener("touch", close); + window.addEventListener("popstate", remove); + + // Initializing the "Back Button" of the "Web App" window + core.telegram.api.BackButton.show(); + core.telegram.api.BackButton.offClick(core.catalog.back); + core.telegram.api.BackButton.onClick(remove); + + // setTimeout(() => { + import("./hotline.mjs").then((hotline) => { + // Imported the hotline module + images.hotline = new hotline.default( + images, + ); + images.hotline.step = -1; + images.hotline.interval = 20; + images.hotline.wheel = true; + images.hotline.touch = true; + images.hotline.events.set("transfer.beginning", true); + images.hotline.events.set("transfer.end", true); + images.hotline.events.set("moved.forward", true); + images.hotline.events.set("moved.backward", true); + + if (width > card.offsetWidth) { + images.hotline.start(); + } + }); + // }, 300); + } + }); + + // Exit (success) + resolve(); + } + } + }, + () => reject(), + ); + } + } catch (e) { + // Exit (fail) + reject(e); + } + }, + }, +); + // Connecting to the core if (!core.catalog) core.catalog = catalog; diff --git a/mirzaev/huesos/system/public/js/modules/loader.mjs b/mirzaev/huesos/system/public/js/modules/loader.mjs index 5c31680..e5d1eea 100755 --- a/mirzaev/huesos/system/public/js/modules/loader.mjs +++ b/mirzaev/huesos/system/public/js/modules/loader.mjs @@ -15,6 +15,11 @@ export default class loader { */ static type = "module"; + /** + * @name Actial URI + */ + static uri; + /** * @name Load * @@ -23,7 +28,7 @@ export default class loader { * * @return {Promise} */ - static async load(uri = "/", body) { + static async load(uri = "/", body, back = false) { if (typeof uri === "string") { // Received and validated uri @@ -54,8 +59,27 @@ export default class loader { } else { // Success (not received errors) - // Writing to the browser history - history.pushState({}, json.title ?? uri, uri); + if (back) { + // Requested to go back + + // Writing actual URI + this.uri = history.state?.previous; + + // Deletimg from the browser history + history.back(); + } else { + // Requested to go forward + + // Writing to the browser history + history.pushState( + { previous: this.uri }, + json.title ?? uri, + uri, + ); + + // Writing actual URI + this.uri = uri; + } /** * The @@ -222,7 +246,10 @@ export default class loader { // Found the last <section> element inside the <body> element // Writing the <main> element after the last <section> element inside the <body> element - body.insertBefore(core.main, section.nextElementSibling); + body.insertBefore( + core.main, + section.nextElementSibling, + ); } else { // Not found section elements <section> inside the <body> element diff --git a/mirzaev/huesos/system/public/js/modules/telegram.mjs b/mirzaev/huesos/system/public/js/modules/telegram.mjs index bcf8ad3..cc962f7 100755 --- a/mirzaev/huesos/system/public/js/modules/telegram.mjs +++ b/mirzaev/huesos/system/public/js/modules/telegram.mjs @@ -21,6 +21,11 @@ export default class telegram { * @see {@link https://core.telegram.org/bots/webapps#initializing-mini-apps} */ static api = window.Telegram.WebApp; + + /** + * @name List of BackButton events + */ + static back = []; } // Connecting to the core diff --git a/mirzaev/huesos/system/public/js/telegram.js b/mirzaev/huesos/system/public/js/telegram.js index 7a84857..dac5e3f 100755 --- a/mirzaev/huesos/system/public/js/telegram.js +++ b/mirzaev/huesos/system/public/js/telegram.js @@ -2,7 +2,7 @@ core.modules.connect(["telegram"]) .then(() => { - // + // Imported the telegram module // Expanding the "Web App" window // core.telegram.api.expand(); @@ -24,6 +24,20 @@ core.modules.connect(["telegram"]) document.documentElement.style.setProperty('--tg-theme-section-bg-color', 'var(--tg-theme-secondary-bg-color)'); } + if (core.telegram.back.length > 0) { + // Initialized BackButton events + + // Initializing the "Back Button" of the "Web App" window + core.telegram.api.BackButton.show(); + + for (const event of core.telegram.back) { + // Iterating over BackButton events + + // Initializing the BackButton event listener + core.telegram.api.BackButton.onClick(event); + } + } + core.modules.connect(["session", "account"]) .then(() => { // diff --git a/mirzaev/huesos/system/public/themes/default/css/catalog.css b/mirzaev/huesos/system/public/themes/default/css/catalog.css index 2400bf1..6f4c106 100755 --- a/mirzaev/huesos/system/public/themes/default/css/catalog.css +++ b/mirzaev/huesos/system/public/themes/default/css/catalog.css @@ -27,10 +27,16 @@ main>section#categories:last-child { /* margin-bottom: unset; */ } -main>section#categories>a.category[type="button"]:has(>img) { +/* main>section#categories>a.category[type="button"]:has(>img) { min-width: calc(50% - var(--padding) * 2); height: 180px; padding: unset; +} */ + +main>section#categories>a.category[type="button"] { + width: calc(50% - var(--padding) * 2); + height: 180px; + padding: unset; } main>section#categories>a.category[type="button"]>img { @@ -48,7 +54,8 @@ main>section#categories>a.category[type="button"]:is(:hover, :focus)>img { filter: unset; } -main>section#categories>a.category[type="button"]:has(>img)>p { +/* main>section#categories>a.category[type="button"]:has(>img)>p { */ +main>section#categories>a.category[type="button"]>p { position: absolute; left: var(--padding); bottom: var(--padding); diff --git a/mirzaev/huesos/system/public/themes/default/css/icons/house.css b/mirzaev/huesos/system/public/themes/default/css/icons/house.css new file mode 100644 index 0000000..efd5a0f --- /dev/null +++ b/mirzaev/huesos/system/public/themes/default/css/icons/house.css @@ -0,0 +1,52 @@ +@charset "UTF-8"; + +i.icon.house { + position: relative; + margin-bottom: -2px; + width: 18px; + height: 14px; + display: block; + box-sizing: border-box; + border: 2px solid; + border-top: 0; + border-bottom: 0; + border-top-right-radius: 3px; + border-top-left-radius: 3px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + background: + linear-gradient(to left, currentColor 5px, transparent 0) no-repeat 0 bottom/4px 2px, + linear-gradient(to left, currentColor 5px, transparent 0) no-repeat right bottom/4px 2px; +} + +i.icon.house::after, +i.icon.house::before { + content: ""; + position: absolute; + display: block; + box-sizing: border-box; +} + +i.icon.house::before { + left: 0; + top: -5px; + width: 14px; + height: 14px; + border-radius: 3px; + transform: rotate(45deg); + border-top: 2px solid; + border-left: 2px solid; + border-top-left-radius: 4px; +} + +i.icon.house::after { + left: 3px; + bottom: 0; + width: 8px; + height: 10px; + border: 2px solid; + border-radius: 100px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: 0; +}