From cf0e32e9546d3b80078ce468617bb7af521cc502 Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Tue, 29 Oct 2024 13:01:15 +0300 Subject: [PATCH] THE JAVASCRIPT SYSTEM TOTAL REBUILD --- composer.json | 5 +- composer.lock | 1126 +++++++++++++- .../arming_bot/system/controllers/account.php | 24 +- .../arming_bot/system/controllers/cart.php | 215 +-- .../arming_bot/system/controllers/catalog.php | 12 +- .../arming_bot/system/controllers/core.php | 6 +- .../system/controllers/deliveries/cdek.php | 78 + .../arming_bot/system/controllers/session.php | 27 +- mirzaev/arming_bot/system/models/cart.php | 2 +- .../system/models/deliveries/cdek.php | 84 + .../system/models/traits/buffer.php | 15 +- mirzaev/arming_bot/system/public/index.php | 1 + .../arming_bot/system/public/js/account.js | 156 -- .../system/public/js/authentication.js | 53 +- mirzaev/arming_bot/system/public/js/cart.js | 430 ------ .../arming_bot/system/public/js/catalog.js | 1159 -------------- .../arming_bot/system/public/js/connection.js | 324 ---- mirzaev/arming_bot/system/public/js/core.js | 160 +- mirzaev/arming_bot/system/public/js/damper.js | 96 -- .../arming_bot/system/public/js/hotline.js | 1361 ++++++++--------- mirzaev/arming_bot/system/public/js/loader.js | 265 ---- .../system/public/js/modules/account.js | 202 +++ .../system/public/js/modules/cart.js | 528 +++++++ .../system/public/js/modules/catalog.js | 1236 +++++++++++++++ .../system/public/js/modules/connection.js | 305 ++++ .../system/public/js/modules/damper.js | 76 + .../system/public/js/modules/delivery.js | 161 ++ .../system/public/js/modules/loader.js | 250 +++ .../system/public/js/modules/session.js | 129 ++ .../system/public/js/modules/telegram.js | 27 + .../arming_bot/system/public/js/session.js | 71 - .../arming_bot/system/public/js/telegram.js | 53 - mirzaev/arming_bot/system/public/robot.php | 10 +- .../system/public/themes/default/css/cart.css | 91 +- .../system/public/themes/default/css/main.css | 14 + .../system/public/themes/default/css/menu.css | 14 +- .../public/themes/default/css/window.css | 24 +- .../system/settings/cdek.php.sample | 6 + .../{key.php.sample => telegram.php.sample} | 0 mirzaev/arming_bot/system/views/templater.php | 6 +- .../system/views/themes/default/account.html | 4 +- .../default/cart/elements/categories.html | 5 - .../default/cart/elements/delivery.html | 25 + .../default/cart/elements/products.html | 5 +- .../themes/default/cart/elements/summary.html | 9 +- .../views/themes/default/cart/page.html | 8 +- .../default/catalog/elements/filters.html | 2 +- .../default/catalog/elements/products.html | 1 - .../default/catalog/elements/search.html | 2 +- .../views/themes/default/catalog/page.html | 8 +- .../views/themes/default/connection.html | 2 +- .../system/views/themes/default/js.html | 38 +- .../system/views/themes/default/menu.html | 2 +- .../default/orcer/elements/delivery.html | 11 + .../default/orcer/elements/summary.html | 11 + .../views/themes/default/orcer/page.html | 24 + 56 files changed, 5456 insertions(+), 3503 deletions(-) create mode 100755 mirzaev/arming_bot/system/controllers/deliveries/cdek.php create mode 100755 mirzaev/arming_bot/system/models/deliveries/cdek.php delete mode 100755 mirzaev/arming_bot/system/public/js/account.js delete mode 100755 mirzaev/arming_bot/system/public/js/cart.js delete mode 100755 mirzaev/arming_bot/system/public/js/catalog.js delete mode 100755 mirzaev/arming_bot/system/public/js/connection.js delete mode 100755 mirzaev/arming_bot/system/public/js/damper.js delete mode 100755 mirzaev/arming_bot/system/public/js/loader.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/account.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/cart.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/catalog.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/connection.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/damper.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/delivery.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/loader.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/session.js create mode 100755 mirzaev/arming_bot/system/public/js/modules/telegram.js delete mode 100755 mirzaev/arming_bot/system/public/js/session.js delete mode 100755 mirzaev/arming_bot/system/public/js/telegram.js create mode 100755 mirzaev/arming_bot/system/settings/cdek.php.sample rename mirzaev/arming_bot/system/settings/{key.php.sample => telegram.php.sample} (100%) delete mode 100755 mirzaev/arming_bot/system/views/themes/default/cart/elements/categories.html create mode 100755 mirzaev/arming_bot/system/views/themes/default/cart/elements/delivery.html create mode 100755 mirzaev/arming_bot/system/views/themes/default/orcer/elements/delivery.html create mode 100755 mirzaev/arming_bot/system/views/themes/default/orcer/elements/summary.html create mode 100755 mirzaev/arming_bot/system/views/themes/default/orcer/page.html diff --git a/composer.json b/composer.json index 4f2e73b..ae4b438 100755 --- a/composer.json +++ b/composer.json @@ -28,7 +28,10 @@ "twig/extra-bundle": "^3.7", "twig/intl-extra": "^3.10", "avadim/fast-excel-reader": "^2.19", - "openswoole/core": "22.1.5" + "openswoole/core": "22.1.5", + "ttatpuot/cdek-sdk2.0": "^1.2", + "guzzlehttp/guzzle": "^7.9", + "php-http/guzzle7-adapter": "^1.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 61dbfab..a3eb079 100755 --- 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": "2a3687f9c81d7a26a57b654136653c18", + "content-hash": "6dd31244ca04cd9ec32a009c8ef467a7", "packages": [ { "name": "avadim/fast-excel-helper", @@ -371,6 +371,129 @@ ], "time": "2024-04-10T14:46:11+00:00" }, + { + "name": "doctrine/annotations", + "version": "1.14.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "253dca476f70808a5aeed3a47cc2cc88c5cab915" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/253dca476f70808a5aeed3a47cc2cc88c5cab915", + "reference": "253dca476f70808a5aeed3a47cc2cc88c5cab915", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^1 || ^2", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "~1.4.10 || ^1.10.28", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.14.4" + }, + "time": "2024-09-05T10:15:52+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + }, + "time": "2024-01-30T19:34:25+00:00" + }, { "name": "doctrine/inflector", "version": "2.0.10", @@ -532,6 +655,84 @@ ], "time": "2022-12-30T00:23:10+00:00" }, + { + "name": "doctrine/lexer", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", + "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/2.1.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:35:39+00:00" + }, { "name": "evenement/evenement", "version": "v3.0.2", @@ -635,6 +836,491 @@ }, "time": "2020-11-24T22:02:12+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "7.9.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2024-07-24T11:22:20+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2024-10-17T10:06:22+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.7.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2024-07-18T11:15:46+00:00" + }, + { + "name": "jms/metadata", + "version": "2.8.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/metadata.git", + "reference": "7ca240dcac0c655eb15933ee55736ccd2ea0d7a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/7ca240dcac0c655eb15933ee55736ccd2ea0d7a6", + "reference": "7ca240dcac0c655eb15933ee55736ccd2ea0d7a6", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "require-dev": { + "doctrine/cache": "^1.0", + "doctrine/coding-standard": "^8.0", + "mikey179/vfsstream": "^1.6.7", + "phpunit/phpunit": "^8.5|^9.0", + "psr/container": "^1.0|^2.0", + "symfony/cache": "^3.1|^4.0|^5.0", + "symfony/dependency-injection": "^3.1|^4.0|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Metadata\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "Class/method/property metadata management in PHP", + "keywords": [ + "annotations", + "metadata", + "xml", + "yaml" + ], + "support": { + "issues": "https://github.com/schmittjoh/metadata/issues", + "source": "https://github.com/schmittjoh/metadata/tree/2.8.0" + }, + "time": "2023-02-15T13:44:18+00:00" + }, + { + "name": "jms/serializer", + "version": "3.29.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/serializer.git", + "reference": "111451f43abb448ce297361a8ab96a9591e848cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/111451f43abb448ce297361a8ab96a9591e848cd", + "reference": "111451f43abb448ce297361a8ab96a9591e848cd", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.14 || ^2.0", + "doctrine/instantiator": "^1.3.1 || ^2.0", + "doctrine/lexer": "^2.0 || ^3.0", + "jms/metadata": "^2.6", + "php": "^7.2 || ^8.0", + "phpstan/phpdoc-parser": "^1.20" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0", + "doctrine/orm": "^2.14 || ^3.0", + "doctrine/persistence": "^2.5.2 || ^3.0", + "doctrine/phpcr-odm": "^1.5.2 || ^2.0", + "ext-pdo_sqlite": "*", + "jackalope/jackalope-doctrine-dbal": "^1.3", + "ocramius/proxy-manager": "^1.0 || ^2.0", + "phpbench/phpbench": "^1.0", + "phpstan/phpstan": "^1.0.2", + "phpunit/phpunit": "^8.5.21 || ^9.0 || ^10.0", + "psr/container": "^1.0 || ^2.0", + "symfony/dependency-injection": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/expression-language": "^3.2 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/form": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/translation": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/uid": "^5.1 || ^6.0 || ^7.0", + "symfony/validator": "^3.1.9 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "twig/twig": "^1.34 || ^2.4 || ^3.0" + }, + "suggest": { + "doctrine/collections": "Required if you like to use doctrine collection types as ArrayCollection.", + "symfony/cache": "Required if you like to use cache functionality.", + "symfony/uid": "Required if you'd like to serialize UID objects.", + "symfony/yaml": "Required if you'd like to use the YAML metadata format." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "JMS\\Serializer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "Library for (de-)serializing data of any complexity; supports XML, and JSON.", + "homepage": "http://jmsyst.com/libs/serializer", + "keywords": [ + "deserialization", + "jaxb", + "json", + "serialization", + "xml" + ], + "support": { + "issues": "https://github.com/schmittjoh/serializer/issues", + "source": "https://github.com/schmittjoh/serializer/tree/3.29.1" + }, + "funding": [ + { + "url": "https://github.com/goetas", + "type": "github" + } + ], + "time": "2023-12-14T15:25:09+00:00" + }, { "name": "laravel/serializable-closure", "version": "v1.3.4", @@ -1356,6 +2042,125 @@ }, "time": "2024-03-29T13:00:05+00:00" }, + { + "name": "php-http/guzzle7-adapter", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/guzzle7-adapter.git", + "reference": "fb075a71dbfa4847cf0c2938c4e5a9c478ef8b01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/guzzle7-adapter/zipball/fb075a71dbfa4847cf0c2938c4e5a9c478ef8b01", + "reference": "fb075a71dbfa4847cf0c2938c4e5a9c478ef8b01", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.0", + "php": "^7.2 | ^8.0", + "php-http/httplug": "^2.0", + "psr/http-client": "^1.0" + }, + "provide": { + "php-http/async-client-implementation": "1.0", + "php-http/client-implementation": "1.0", + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.0|^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Adapter\\Guzzle7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "Guzzle 7 HTTP Adapter", + "homepage": "http://httplug.io", + "keywords": [ + "Guzzle", + "http" + ], + "support": { + "issues": "https://github.com/php-http/guzzle7-adapter/issues", + "source": "https://github.com/php-http/guzzle7-adapter/tree/1.0.0" + }, + "time": "2021-03-09T07:35:15+00:00" + }, + { + "name": "php-http/httplug", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/httplug.git", + "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/httplug/zipball/5cad731844891a4c282f3f3e1b582c46839d22f4", + "reference": "5cad731844891a4c282f3f3e1b582c46839d22f4", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/promise": "^1.1", + "psr/http-client": "^1.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "support": { + "issues": "https://github.com/php-http/httplug/issues", + "source": "https://github.com/php-http/httplug/tree/2.4.1" + }, + "time": "2024-09-23T11:39:58+00:00" + }, { "name": "php-http/multipart-stream-builder", "version": "1.4.2", @@ -1412,6 +2217,105 @@ }, "time": "2024-09-04T13:22:54+00:00" }, + { + "name": "php-http/promise", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/promise.git", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "friends-of-phpspec/phpspec-code-coverage": "^4.3.2 || ^6.3", + "phpspec/phpspec": "^5.1.2 || ^6.2 || ^7.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/php-http/promise/issues", + "source": "https://github.com/php-http/promise/tree/1.3.1" + }, + "time": "2024-03-15T13:55:21+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.33.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" + }, + "time": "2024-10-13T11:25:22+00:00" + }, { "name": "psr/cache", "version": "3.0.0", @@ -1559,6 +2463,58 @@ }, "time": "2019-01-08T18:20:26+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, { "name": "psr/http-factory", "version": "1.1.0", @@ -1830,6 +2786,96 @@ }, "time": "2021-05-03T11:20:27+00:00" }, + { + "name": "rakit/validation", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/rakit/validation.git", + "reference": "ff003a35cdf5030a5f2482299f4c93f344a35b29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rakit/validation/zipball/ff003a35cdf5030a5f2482299f4c93f344a35b29", + "reference": "ff003a35cdf5030a5f2482299f4c93f344a35b29", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=7.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^6.5", + "squizlabs/php_codesniffer": "^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Rakit\\Validation\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Muhammad Syifa", + "email": "emsifa@gmail.com" + } + ], + "description": "PHP Laravel like standalone validation library", + "support": { + "issues": "https://github.com/rakit/validation/issues", + "source": "https://github.com/rakit/validation/tree/v1.4.0" + }, + "time": "2020-08-27T05:07:01+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, { "name": "react/cache", "version": "v1.2.0", @@ -5067,6 +6113,84 @@ }, "time": "2021-06-18T12:06:02+00:00" }, + { + "name": "ttatpuot/cdek-sdk2.0", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/TTATPuOT/cdek-sdk2.0.git", + "reference": "52fafad201682ea4e4ab5ee166749271bd32e40e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TTATPuOT/cdek-sdk2.0/zipball/52fafad201682ea4e4ab5ee166749271bd32e40e", + "reference": "52fafad201682ea4e4ab5ee166749271bd32e40e", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.14.3", + "ext-json": "*", + "jms/serializer": "^3 <3.30", + "nyholm/psr7": "^1.2", + "php": "^7.1 || ^8.0", + "psr/http-client": "^1.0", + "rakit/validation": "^1.1" + }, + "require-dev": { + "phan/phan": "^1 <1.3 || >1.3.0", + "php-coveralls/php-coveralls": "^2.1", + "php-http/mock-client": "^1.3", + "phpmd/phpmd": "^2.7", + "phpstan/phpstan": ">=0.10", + "phpunit/phpunit": "^8.5 || ^9.3", + "squizlabs/php_codesniffer": "*", + "symfony/http-client": "^5.4.22", + "vimeo/psalm": ">=3.0.16" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "v0.0.1-dev" + } + }, + "autoload": { + "psr-4": { + "CdekSDK2\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Viktor Chizhekov", + "homepage": "https://github.com/ihomyak", + "role": "Creator" + }, + { + "name": "Anton Neverov", + "homepage": "https://github.com/TTATPuOT", + "role": "Contributor" + } + ], + "description": "Рабочий и поддерживаемый PHP SDK для API v2.0 СДЭК", + "homepage": "https://www.cdek.ru/clients/integrator.html", + "keywords": [ + "cdek", + "delivery", + "sdk", + "v2.0", + "СДЭК", + "служба доставки" + ], + "support": { + "docs": "https://github.com/ttatpuot/cdek-sdk2.0/blob/master/README.md", + "issues": "https://github.com/ttatpuot/cdek-sdk2.0/issues", + "source": "https://github.com/TTATPuOT/cdek-sdk2.0/tree/1.2.0" + }, + "time": "2024-03-07T14:45:10+00:00" + }, { "name": "twig/extra-bundle", "version": "v3.13.0", diff --git a/mirzaev/arming_bot/system/controllers/account.php b/mirzaev/arming_bot/system/controllers/account.php index f2049fa..e0e98ad 100755 --- a/mirzaev/arming_bot/system/controllers/account.php +++ b/mirzaev/arming_bot/system/controllers/account.php @@ -40,23 +40,31 @@ final class account extends core public function write(array $parameters = []): void { if (!empty($parameters) && $this->account instanceof model) { - // Found data of the program and active account + // Received parameters and initialized account + + // Declaring the buffer of deserialized parameters + $deserialized = []; foreach ($parameters as $name => $value) { // Iterate over parameters - // Validation of the parameter + // Validation of the parameter value if (mb_strlen($value) > 4096) continue; - // Convert name to multidimensional array - foreach (array_reverse(explode('_', $name)) as $key) $parameter = [$key => $parameter ?? json_validate($value) ? json_decode($value, true, 10) : $value]; + // Declaring the buffer of deserialized parameter + $parameter = null; - // Write data of to the buffer parameter in the implement object of account document from ArangoDB - $this->account->buffer = $parameter + $this->account->buffer ?? []; + // Deserializing name to multidimensional array + foreach (array_reverse(explode('_', $name)) as $key) + $parameter = [$key => $parameter ?? (json_validate($value) ? json_decode($value, true, 10) : $value)]; + + // Writing into the buffer of deserialized parameters + $deserialized = array_merge_recursive($parameter, $deserialized); } - // Write from implement object to account document from ArangoDB - document::update($this->account->__document(), $this->errors['account']); + + // Write to the account document from ArangoDB + if (!empty($deserialized)) $this->account->write($deserialized, $this->errors['account']); } } } diff --git a/mirzaev/arming_bot/system/controllers/cart.php b/mirzaev/arming_bot/system/controllers/cart.php index d9c2639..d0b9453 100755 --- a/mirzaev/arming_bot/system/controllers/cart.php +++ b/mirzaev/arming_bot/system/controllers/cart.php @@ -24,6 +24,11 @@ use mirzaev\arangodb\document; */ final class cart extends core { + /** + * Instance of the cart + */ + protected readonly ?model $cart; + /** * Registry of errors */ @@ -57,6 +62,22 @@ final class cart extends core ); } + // Initializing the cart + $this->cart = $this->account?->cart() ?? $this->session?->cart(); + + // Initializing the cart data + $this->view->cart = [ + 'summary' => $this->cart?->summary(currency: $this->currency), + 'products' => $this->cart?->products(language: $this->language, currency: $this->currency) + ]; + + // Initializing types of avaiabld deliveries + $this->view->deliveries = [ + 'cdek' => [ + 'label' => 'CDEK' + ] + ]; + if ($_SERVER['REQUEST_METHOD'] === 'GET') { // GET request @@ -135,93 +156,100 @@ final class cart extends core if ($product instanceof product) { // Initialized the product - // Initializing the buffer with amount of the product in the cart - $amount = $this->cart->count(product: $product, limit: 100, errors: $this->errors['cart']) ?? 0; + // Initializing the cart + $this->cart = $this->account?->cart() ?? $this->session?->cart(); if ($this->cart instanceof model) { // Initialized the cart - // Validating @todo add throwing errors - $type = null; - if (isset($parameters['type']) && preg_match('/[\w]+/', urldecode($parameters['type']), $matches)) $type = $matches[0]; + // Initializing the buffer with amount of the product in the cart + $amount = $this->cart->count(product: $product, limit: 100, errors: $this->errors['cart']) ?? 0; - if (isset($type)) { - // Received and validated type of action with the product + if ($this->cart instanceof model) { + // Initialized the cart - if ($type === 'toggle') { - // Write the product to the cart if is not in the cart and vice versa + // Validating @todo add throwing errors + $type = null; + if (isset($parameters['type']) && preg_match('/[\w]+/', urldecode($parameters['type']), $matches)) $type = $matches[0]; - if ($amount > 0) { - // The cart has the product + if (isset($type)) { + // Received and validated type of action with the product - // Deleting the product from the cart - $this->cart->delete(product: $product, amount: $amount, errors: $this->errors['cart']); + if ($type === 'toggle') { + // Write the product to the cart if is not in the cart and vice versa - // Reinitializing the buffer with amount of the product in the cart - $amount = 0; + if ($amount > 0) { + // The cart has the product + + // Deleting the product from the cart + $this->cart->delete(product: $product, amount: $amount, errors: $this->errors['cart']); + + // Reinitializing the buffer with amount of the product in the cart + $amount = 0; + } else { + // The cart has no the product + + // Writing the product to the cart + $this->cart->write(product: $product, amount: 1, errors: $this->errors['cart']); + + // Reinitializing the buffer with amount of the product in the cart + $amount = 1; + } } else { - // The cart has no the product + // Received not the "toggle" command - // Writing the product to the cart - $this->cart->write(product: $product, amount: 1, errors: $this->errors['cart']); + // Validating @todo add throwing errors + $_amount = null; + if (isset($parameters['amount']) && preg_match('/[\d]+/', urldecode($parameters['amount']), $matches)) $_amount = (int) $matches[0]; - // Reinitializing the buffer with amount of the product in the cart - $amount = 1; - } - } else { - // Received not the "toggle" command + if (isset($_amount)) { + // Received and validated amount parameter for action with the product - // Validating @todo add throwing errors - $_amount = null; - if (isset($parameters['amount']) && preg_match('/[\d]+/', urldecode($parameters['amount']), $matches)) $_amount = (int) $matches[0]; + if ($type === 'write') { + // Increase amount of the product in the cart - if (isset($_amount)) { - // Received and validated amount parameter for action with the product + if ($amount + $_amount < 101) { + // Validated amount to wrting - if ($type === 'write') { - // Increase amount of the product in the cart + // Writing the product to the cart + $this->cart->write(product: $product, amount: $_amount, errors: $this->errors['cart']); - if ($amount + $_amount < 101) { - // Validated amount to wrting + // Reinitialize the buffer with amount of the product in the cart + $amount += $_amount; + } + } else if ($type === 'delete') { + // Decrease amount of the product in the cart - // Writing the product to the cart - $this->cart->write(product: $product, amount: $_amount, errors: $this->errors['cart']); - - // Reinitialize the buffer with amount of the product in the cart - $amount += $_amount; - } - } else if ($type === 'delete') { - // Decrease amount of the product in the cart - - if ($amount - $_amount > -1) { - // Validated amount to deleting - - // Deleting the product from the cart - $this->cart->delete(product: $product, amount: $_amount, errors: $this->errors['cart']); - - // Reinitialize the buffer with amount of the product in the cart - $amount -= $_amount; - } - } else if ($type === 'set') { - // Set amount of the product in the cart - - if ($_amount > -1 && $_amount < 101) { - // Validated amount to setting - - if ($_amount > $amount) { - // Requested amount more than actual amount of the product in the cart - - // Writing the product from the cart - $this->cart->write(product: $product, amount: $_amount - $amount, errors: $this->errors['cart']); - } else { - // Requested amount less than actual amount of the product in the cart + if ($amount - $_amount > -1) { + // Validated amount to deleting // Deleting the product from the cart - $this->cart->delete(product: $product, amount: $amount - $_amount, errors: $this->errors['cart']); - } + $this->cart->delete(product: $product, amount: $_amount, errors: $this->errors['cart']); - // Reinitializing the buffer with amount of the product in the cart - $amount = $_amount; + // Reinitialize the buffer with amount of the product in the cart + $amount -= $_amount; + } + } else if ($type === 'set') { + // Set amount of the product in the cart + + if ($_amount > -1 && $_amount < 101) { + // Validated amount to setting + + if ($_amount > $amount) { + // Requested amount more than actual amount of the product in the cart + + // Writing the product from the cart + $this->cart->write(product: $product, amount: $_amount - $amount, errors: $this->errors['cart']); + } else { + // Requested amount less than actual amount of the product in the cart + + // Deleting the product from the cart + $this->cart->delete(product: $product, amount: $amount - $_amount, errors: $this->errors['cart']); + } + + // Reinitializing the buffer with amount of the product in the cart + $amount = $_amount; + } } } } @@ -272,35 +300,42 @@ final class cart extends core if ($_SERVER['REQUEST_METHOD'] === 'POST') { // POST request - // Initializing summary data of the cart - $summary = $this->cart?->summary(currency: $this->currency, errors: $this->errors['cart']); + // Initializing the cart + $this->cart = $this->account?->cart() ?? $this->session?->cart(); - // Initializing response headers - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + if ($this->cart instanceof model) { + // Initialized the cart - // Initializing of the output buffer - ob_start(); + // Initializing summary data of the cart + $summary = $this->cart?->summary(currency: $this->currency, errors: $this->errors['cart']); - // Generating the reponse - echo json_encode( - [ - 'cost' => $summary['cost'] ?? 0, - 'amount' => $summary['amount'] ?? 0, - 'errors' => $this->errors - ] - ); + // Initializing response headers + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Initializing a response headers - header('Content-Length: ' . ob_get_length()); + // Initializing of the output buffer + ob_start(); - // Sending and deinitializing of the output buffer - ob_end_flush(); - flush(); + // Generating the reponse + echo json_encode( + [ + 'cost' => $summary['cost'] ?? 0, + 'amount' => $summary['amount'] ?? 0, + 'errors' => $this->errors + ] + ); - // Exit (success) - return null; + // Initializing a response headers + header('Content-Length: ' . ob_get_length()); + + // Sending and deinitializing of the output buffer + ob_end_flush(); + flush(); + + // Exit (success) + return null; + } } // Exit (fail) diff --git a/mirzaev/arming_bot/system/controllers/catalog.php b/mirzaev/arming_bot/system/controllers/catalog.php index a9cf5bb..1b06b95 100755 --- a/mirzaev/arming_bot/system/controllers/catalog.php +++ b/mirzaev/arming_bot/system/controllers/catalog.php @@ -53,7 +53,7 @@ final class catalog extends core filter: "d.identifier == @identifier && d.deleted != true && d.hidden != true", sort: 'd.created DESC', amount: 1, - return: '{identifier: d.identifier, name: d.name.@language, description: d.description.@language, cost: d.cost, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, cost: d.cost.@currency, images: d.images[*].storage}', + return: '{identifier: d.identifier, name: d.name.@language, description: d.description.@language, cost: d.cost.@currency, weight: d.weight, dimensions: d.dimensions, brand: d.brand.@language, compatibility: d.compatibility.@language, cost: d.cost.@currency, images: d.images[*].storage}', language: $this->language, currency: $this->currency, parameters: ['identifier' => $product], @@ -184,9 +184,9 @@ final class catalog extends core if (isset($parameters['text']) && preg_match('/[\w\s]+/u', urldecode($parameters['text']), $matches) && mb_strlen($matches[0]) > 2) $text = $matches[0]; if (isset($text)) { - // Received and validated text + // Received and validated text - // Writing to the account buffer + // Writing to the account buffer (useless becouse rewrite itself to null with every request) $this->account?->write( [ 'catalog' => [ @@ -197,7 +197,7 @@ final class catalog extends core ] ); - // Writing to the session buffer + // Writing to the session buffer (useless becouse rewrite itself to null with every request) $this->session?->write( [ 'catalog' => [ @@ -296,7 +296,7 @@ final class catalog extends core ) ?? null; } - if (isset($brand) || (isset($this->view->products) && count($this->view->products) > 0)) { + if (isset($brand) || isset($text) || (isset($this->view->products) && count($this->view->products) > 0)) { // Received and validated at least one of filters or amount of rendered products is more than 0 // Search for filters and write to the buffer of global variables of view templater @@ -355,7 +355,7 @@ final class catalog extends core sprintf( << core.catalog.product_system('%s'), 500); + _window = setTimeout(() => core.catalog.product.system('%s'), 500); } javascript, $this->view->product['identifier'] diff --git a/mirzaev/arming_bot/system/controllers/core.php b/mirzaev/arming_bot/system/controllers/core.php index 0b91235..ad36da0 100755 --- a/mirzaev/arming_bot/system/controllers/core.php +++ b/mirzaev/arming_bot/system/controllers/core.php @@ -138,15 +138,11 @@ class core extends controller // Initializing of the currency $this->currency = $this->account?->currency ?? $this->session?->buffer['currency'] ?? $this->settings?->currency ?? currency::usd; - // Initializing of the cart - $this->cart = $this->account?->cart() ?? $this->session?->cart(); - // Initializing of preprocessor of views $this->view = new templater( session: $this->session, account: $this->account, - settings: $this->settings, - cart: $this->cart + settings: $this->settings ); // @todo перенести в middleware diff --git a/mirzaev/arming_bot/system/controllers/deliveries/cdek.php b/mirzaev/arming_bot/system/controllers/deliveries/cdek.php new file mode 100755 index 0000000..a5b735a --- /dev/null +++ b/mirzaev/arming_bot/system/controllers/deliveries/cdek.php @@ -0,0 +1,78 @@ + + */ +final class cdek extends core +{ + /** + * Registry of errors + */ + protected array $errors = [ + 'delivery' => [] + ]; + + /** + * Calculate + * + * @param array $parameters Parameters of the request (POST + GET) + */ + public function calculate(array $parameters = []): ?string + { + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + // POST request + + $this->model::calculate(); + + die; + + // Initializing a response headers + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Initializing of the output buffer + ob_start(); + + // Generating the reponse + echo json_encode( + [ + 'main' => '', + 'errors' => $this->errors + ] + ); + + // Initializing a response headers + header('Content-Length: ' . ob_get_length()); + + // Sending and deinitializing of the output buffer + ob_end_flush(); + flush(); + + // Exit (success) + return null; + } + + // Exit (fail) + return null; + } +} diff --git a/mirzaev/arming_bot/system/controllers/session.php b/mirzaev/arming_bot/system/controllers/session.php index 5ac2ce7..6f9fcff 100755 --- a/mirzaev/arming_bot/system/controllers/session.php +++ b/mirzaev/arming_bot/system/controllers/session.php @@ -161,29 +161,36 @@ final class session extends core * @param array $parameters Parameters of the request (POST + GET) * * @return void - * - * @todo переделать под trait buffer + * + * @todo переделать под trait buffer */ public function write(array $parameters = []): void { if (!empty($parameters) && $this->session instanceof model) { - // Found data of the program and active session + // Received parameters and initialized session + + // Declaring the buffer of deserialized parameters + $deserialized = []; foreach ($parameters as $name => $value) { // Iterate over parameters - // Validation of the parameter + // Validation of the parameter value if (mb_strlen($value) > 4096) continue; - // Convert name to multidimensional array - foreach (array_reverse(explode('_', $name)) as $key) $parameter = [$key => $parameter ?? json_validate($value) ? json_decode($value, true, 10) : $value]; + // Declaring the buffer of deserialized parameter + $parameter = null; - // Write data of to the buffer parameter in the implement object of session document from ArangoDB - $this->session->buffer = $parameter + ($this->session->buffer ?? []); + // Deserializing name to multidimensional array + foreach (array_reverse(explode('_', $name)) as $key) + $parameter = [$key => $parameter ?? (json_validate($value) ? json_decode($value, true, 10) : $value)]; + + // Writing into the buffer of deserialized parameters + $deserialized = array_merge_recursive($parameter, $deserialized); } - // Write from implement object to session document from ArangoDB - document::update($this->session->__document(), $this->errors['session']); + // Write to the session document from ArangoDB + if (!empty($deserialized)) $this->session->write($deserialized, $this->errors['session']); } } } diff --git a/mirzaev/arming_bot/system/models/cart.php b/mirzaev/arming_bot/system/models/cart.php index 543154b..49e9d7b 100755 --- a/mirzaev/arming_bot/system/models/cart.php +++ b/mirzaev/arming_bot/system/models/cart.php @@ -106,7 +106,7 @@ final class cart extends core implements document_interface, collection_interfac } // Exit (success) - return $products; + return isset($products['amount']) ? [$products['document']['_id'] => $products] : $products; } else throw new exception('Failed to initialize ' . product::TYPE . ' collection: ' . product::COLLECTION); } else throw new exception('Failed to initialize ' . reservation::TYPE . ' collection: ' . reservation::COLLECTION); } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); diff --git a/mirzaev/arming_bot/system/models/deliveries/cdek.php b/mirzaev/arming_bot/system/models/deliveries/cdek.php new file mode 100755 index 0000000..813b00d --- /dev/null +++ b/mirzaev/arming_bot/system/models/deliveries/cdek.php @@ -0,0 +1,84 @@ + + */ +final class cdek extends core implements document_interface, collection_interface +{ + use document_trait; + + /** + * Name of the collection in ArangoDB + */ + final public const string COLLECTION = 'delivery'; + + /** + * Calculate + * + * Calculate delivery by CDEK + * + * @param array &$errors Registry of errors + * + * @return + */ + public static function calculate(array &$errors = []): static|null + { + try { + // + /* $client = new client(new guzzle, 'account', 'secure'); */ + $client = new client(new guzzle); + $client->setTest(true); + + $result = $client->cities()->getFiltered(['country_codes' => 'RU', 'city' => 'зеленогорск']); + + if ($result->isOk()) { + // + + //Запрос успешно выполнился + $cities = $client->formatResponseList($result, cities::class); + + foreach ($cities->items as $city) { + var_dump($city); + } + + die; + } + } catch (exception $e) { + // Writing to the registry of errors + $errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return null; + } +} diff --git a/mirzaev/arming_bot/system/models/traits/buffer.php b/mirzaev/arming_bot/system/models/traits/buffer.php index eef2096..bc91a19 100755 --- a/mirzaev/arming_bot/system/models/traits/buffer.php +++ b/mirzaev/arming_bot/system/models/traits/buffer.php @@ -5,7 +5,9 @@ declare(strict_types=1); namespace mirzaev\arming_bot\models\traits; // Files of the project -use mirzaev\arming_bot\models\interfaces\collection as collection_interface; +use mirzaev\arming_bot\models\interfaces\collection as collection_interface, + mirzaev\arming_bot\models\enumerations\language, + mirzaev\arming_bot\models\enumerations\currency; // Library for ArangoDB use ArangoDBClient\Document as _document; @@ -47,12 +49,19 @@ trait buffer if (collection::initialize(static::COLLECTION, static::TYPE, errors: $errors)) { // Initialized the collection - // The instance of the document from ArangoDB is initialized? - isset($this->document) || throw new exception('The instance of the sessoin document from ArangoDB is not initialized'); + // Is the instance of the document from ArangoDB are initialized? + if (!isset($this->document)) throw new exception('The instance of the sessoin document from ArangoDB is not initialized'); // Writing data into buffer of the instance of the document from ArangoDB $this->document->buffer = array_replace_recursive($this->document->buffer ?? [], $data); + // Is the buffer of the instance of the document from ArangoDB exceed 10 megabytes? + if (mb_strlen(json_encode($this->document->buffer)) > 10485760) throw new exception('The buffer size exceeds 10 megabytes'); + + // Serializing parameters + if ($this->document->language instanceof language) $this->document->language = $this->document->language->name; + if ($this->document->currency instanceof currency) $this->document->currency = $this->document->currency->name; + // Writing to ArangoDB and exit (success) return document::update($this->document, errors: $errors); } else throw new exception('Failed to initialize ' . static::TYPE . ' collection: ' . static::COLLECTION); diff --git a/mirzaev/arming_bot/system/public/index.php b/mirzaev/arming_bot/system/public/index.php index 28b4b21..3f1aa64 100755 --- a/mirzaev/arming_bot/system/public/index.php +++ b/mirzaev/arming_bot/system/public/index.php @@ -47,6 +47,7 @@ $router ->write('/account/write', 'account', 'write', 'POST') ->write('/session/write', 'session', 'write', 'POST') ->write('/session/connect/telegram', 'session', 'telegram', 'POST') + ->write('/cdek/calculate', 'deliveries\\cdek', 'calculate', 'POST') /* ->write('/category/$identifier', 'catalog', 'index', 'POST') */ /* ->write('/category', 'catalog', 'index', 'POST') */ /* ->write('/product/$identifier', 'catalog', 'product', 'POST') */ diff --git a/mirzaev/arming_bot/system/public/js/account.js b/mirzaev/arming_bot/system/public/js/account.js deleted file mode 100755 index a2f5e26..0000000 --- a/mirzaev/arming_bot/system/public/js/account.js +++ /dev/null @@ -1,156 +0,0 @@ -"use strict"; - -// Import dependencies -import("/js/core.js").then(() => - import("/js/damper.js").then(() => - import("/js/telegram.js").then(() => { - const dependencies = setInterval(() => { - if ( - typeof core === "function" && - typeof core.damper === "function" && - typeof core.telegram === "function" - ) { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); - - function initialization() { - if (typeof core.account === "undefined") { - // Not initialized - - /** - * @name Account - * - * @description - * Implements actions with accounts - * - * @memberof core - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich - */ - core.account = class account { - // Wrap of indicator of the account - static wrap = document.getElementById("account"); - - // Indicator of the account - static indicator = this.wrap.getElementsByTagName("i")[0]; - - // Description of the account - static description = this.wrap.getElementsByTagName("small")[0]; - - // Statuc of the account - static connected = false; - - // Duration of the disconnected status - static timeout = 0; - - // Instance of the time counter in disconnected status - static counter; - - // Socket address (xn--e1ajlli это сокет) - static socket = "wss://arming.dev.mirzaev.sexy:9502"; - - // Instance of account to the socket - static session; - - // Iterval for reconnect - static interval; - - // Attempts to connect (when core.account.readyState === 0) - static attempts = 0; - - // Interval for block - static block; - - /** - * Authentication - * - * @return {void} - */ - static authentication() { - core.status_loading.removeAttribute("disabled"); - - const timer_for_response = setTimeout(() => { - core.status_loading.setAttribute("disabled", true); - }, 3000); - - if (core.telegram.api.initData.length > 0) { - core - .request( - "/session/connect/telegram", - core.telegram.api.initData, - ) - .then((json) => { - if ( - json.errors !== null && - typeof json.errors === "object" && - json.errors.length > 0 - ) { - // Errors received - } else { - // Errors not received - - if (json.connected === true) { - core.status_loading.setAttribute("disabled", true); - clearTimeout(timer_for_response); - - const a = - core.status_account.getElementsByTagName("a")[0]; - a.setAttribute("onclick", "core.account.profile()"); - a.innerText = json.domain.length > 0 - ? "@" + json.domain - : "ERROR"; - } - - if ( - json.language !== null && - typeof json.language === "string" && - json.langiage.length === 2 - ) { - core.language = json.language; - } - } - }); - } - } - - /** - * Buffer - */ - static buffer = class buffer { - /** - * Write to the account buffer - * - * @param {string} name Name of the parameter - * @param {string} value Value of the parameter (it can be JSON) - * - * @return {void} - */ - static write(name, value) { - if (typeof name === "string" && typeof value === "string") { - // - - // Send request to the server - core.request( - "/account/write", - `${name}=${value}`, - "POST", - {}, - null, - ); - } - } - }; - }; - } - } - }) - ) -); diff --git a/mirzaev/arming_bot/system/public/js/authentication.js b/mirzaev/arming_bot/system/public/js/authentication.js index db1cefc..d44735c 100755 --- a/mirzaev/arming_bot/system/public/js/authentication.js +++ b/mirzaev/arming_bot/system/public/js/authentication.js @@ -1,45 +1,20 @@ "use strict"; -// Import dependencies -import("/js/core.js").then(() => - import("/js/damper.js").then(() => - import("/js/telegram.js").then(() => - import("/js/account.js").then(() => { - const dependencies = setInterval(() => { - if ( - typeof core === "function" && - typeof core.damper === "function" && - typeof core.telegram === "function" && - typeof core.account === "function" - ) { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); +core.modules.connect(["session", "account", "telegram"]) + .then(() => { + // + const { initData, initDataUnsafe, ...data } = core.telegram.api; - function initialization() { - // - const { initData, initDataUnsafe, ...data } = core.telegram.api; + // + core.session.buffer.write("telegram_program", JSON.stringify(data)); - // - core.session.buffer.write("telegram_program", JSON.stringify(data)); + if (core.telegram.api.initData.length > 0) { + // - if (core.telegram.api.initData.length > 0) { - // + // + core.account.authentication(); + } - // - core.account.authentication(); - } - - // - core.telegram.api.ready(); - } - }) - ) - ) -); + // + core.telegram.api.ready(); + }); diff --git a/mirzaev/arming_bot/system/public/js/cart.js b/mirzaev/arming_bot/system/public/js/cart.js deleted file mode 100755 index 757253e..0000000 --- a/mirzaev/arming_bot/system/public/js/cart.js +++ /dev/null @@ -1,430 +0,0 @@ -"use strict"; - -// Import dependencies -import("/js/core.js").then(() => - import("/js/damper.js").then(() => { - const dependencies = setInterval(() => { - if ( - typeof core === "function" && - typeof core.damper === "function" - ) { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); - - function initialization() { - if (typeof core.cart === "undefined") { - // Not initialized - - /** - * @name Cart - * - * @description - * Implements actions with cart - * - * @memberof core - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich - */ - core.cart = class cart { - /** - * Toggle - * - * Toggle the product in the cart (interface) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product - * @param {HTMLElement} product The product - * @param {bool} remove Remove the product element if json.amount === 0? - * @param {bool} force Ignore the damper? (false) - * - * @return {bool} True if an error occurs to continue the event execution - */ - static toggle(element, product, remove = false, force = false) { - // Blocking the element - element.setAttribute("disabled", "true"); - - // Execute under damper - this.toggle_damper( - element, - product, - "toggle", - undefined, - remove, - force, - ); - - // Exit (success) - return false; - } - - /** - * Toggle - * - * Toggle the product in the cart (damper) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product - * @param {HTMLElement} product The product - * @param {bool} remove Remove the product element if json.amount === 0? - * @param {bool} force Ignore the damper? (false) - * - * @return {Promise} - */ - static toggle_damper = core.damper( - (...variables) => this.product(...variables).then(this.summary), - 300, - 6, - ); - - /** - * Write - * - * Write the product in the cart (interface) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product - * @param {HTMLElement} product The product - * @param {number} amount Amount of writings - * @param {bool} remove Remove the product element if json.amount === 0? - * @param {bool} force Ignore the damper? (false) - * - * @return {bool} True if an error occurs to continue the event execution - */ - static write( - element, - product, - amount = 1, - remove = false, - force = false, - ) { - // Blocking the element - element.setAttribute("disabled", "true"); - - // Execute under damper - this.write_damper(element, product, "write", amount, remove, force); - - // Exit (success) - return false; - } - - /** - * Write - * - * Write the product in the cart (damper) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product - * @param {HTMLElement} product The product - * @param {number} amount Amount of writings - * @param {bool} remove Remove the product element if json.amount === 0? - * @param {bool} force Ignore the damper? (false) - * - * @return {Promise} - */ - static write_damper = core.damper( - (...variables) => this.product(...variables).then(this.summary), - 300, - 6, - ); - - /** - * Delete - * - * Delete the product from the cart (interface) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product - * @param {HTMLElement} product The product - * @param {number} amount Amount of deletings - * @param {bool} remove Remove the product element if json.amount === 0? - * @param {bool} force Ignore the damper? (false) - * - * @return {bool} True if an error occurs to continue the event execution - */ - static delete( - element, - product, - amount = 1, - remove = false, - force = false, - ) { - // Blocking the element - element.setAttribute("disabled", "true"); - - // Execute under damper - this.delete_damper( - element, - product, - "delete", - amount, - remove, - force, - ); - - // Exit (success) - return false; - } - - /** - * Delete - * - * Delete the product from the cart (damper) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product - * @param {number} amount Amount of deletings - * @param {bool} remove Remove the product element if json.amount === 0? - * @param {bool} force Ignore the damper? (false) - * - * @return {Promise} - */ - static delete_damper = core.damper( - (...variables) => this.product(...variables).then(this.summary), - 300, - 6, - ); - - /** - * Set - * - * Set amount of the product in the cart (interface) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product - * @param {HTMLElement} product The product - * @param {number} amount Amount of the product in the cart to be setted - * @param {bool} remove Remove the product element if json.amount === 0? - * @param {bool} force Ignore the damper? (false) - * - * @return {bool} True if an error occurs to continue the event execution - */ - static set( - element, - product, - amount = 1, - remove = false, - force = false, - ) { - // Blocking the element - element.setAttribute("disabled", "true"); - - // Execute under damper - this.set_damper(element, product, "set", amount, remove, force); - - // Exit (success) - return false; - } - - /** - * Set - * - * Set the product in the cart (damper) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product - * @param {HTMLElement} product The product - * @param {number} amount Amount of the product in the cart to be setted - * @param {bool} remove Remove the product element if json.amount === 0? - * @param {bool} force Ignore the damper? (false) - * - * @return {Promise} - */ - static set_damper = core.damper( - (...parameters) => this.product(...parameters).then(this.summary), - 300, - 6, - ); - - /** - * The product - * - * Handle the product in the cart (system) - * - * @param {HTMLButtonElement|HTMLInputElement} element Handler element of the product - * @param {HTMLElement} product The product element - * @param {string} type Type of action with the product - * @param {number} amount Amount of product to handle - * @param {bool} remove Remove the product element if json.amount === 0? - * - * @return {Promise|null} - */ - static async product( - element, - product, - type, - amount = null, - remove = false, - resolve = () => {}, - reject = () => {}, - ) { - if ( - (element instanceof HTMLButtonElement || - element instanceof HTMLInputElement) && - product instanceof HTMLElement - ) { - // Validated - - // Initializing the buffer of request body - let request = ""; - - // Initializing of identifier of the product - const identifier = +product.getAttribute( - "data-product-identifier", - ); - - if (typeof identifier === "number") { - // Validated identifier - - // Writing to the buffer of request body - request += "&identifier=" + identifier; - - if ( - type === "toggle" || - type === "write" || - type === "delete" || - type === "set" - ) { - // Validated type - - // Writing to the buffer of request body - request += "&type=" + type; - - if ( - (type === "toggle" && - typeof amount === "undefined") || - (type === "set" && - amount === 0 || - amount === 100) || - typeof amount === "number" && - amount > 0 && - amount < 100 - ) { - // Validated amount - - if (type !== "toggle") { - // Not a toggle request - - // Writing to the buffer of request body - request += "&amount=" + amount; - } - - // Request - return await core.request( - "/cart/product", - request, - ) - .then((json) => { - if ( - json.errors !== null && - typeof json.errors === "object" && - json.errors.length > 0 - ) { - // Fail (received errors) - } else { - // Success (not received errors) - - if (remove && json.amount === 0) { - // Requested deleting of the product element when there is no the product in the cart - - // Deleting the product element - product.remove(); - } else { - // Not requested deleting the product element when there is no the product in the cart - - // Unblocking the element - element.removeAttribute("disabled"); - - // Writing offset of hue-rotate to indicate that the product is in the cart - product.style.setProperty( - "--hue-rotate-offset", - json.amount + "0deg", - ); - - // Writing attribute with amount of the product in the cart - product.setAttribute( - "data-product-amount", - json.amount, - ); - - // Initializing the amount element - const amounts = product.querySelectorAll( - '[data-product-parameter="amount"]', - ); - - for (const amount of amounts) { - // Iterating over an amount elements - - if (amount instanceof HTMLInputElement) { - // The element - - // Writing amount of the product in the cart - amount.value = json.amount; - } else { - // Not the element - - // Writing amount of the product in the cart - amount.innerText = json.amount; - } - } - - // Exit (success) - resolve(); - } - } - }); - } - } - } - } - - // Exit (fail) - reject(); - } - - /** - * Summary - * - * Initialize summary of products the cart (system) - * - * @return {void} - */ - static async summary() { - // Request - return await core.request("/cart/summary") - .then((json) => { - if ( - json.errors !== null && - typeof json.errors === "object" && - json.errors.length > 0 - ) { - // Fail (received errors) - } else { - // Success (not received errors) - - // Initializing the summary amount element - const amount = document.getElementById("amount"); - - // Initializing the summary cost element - const cost = document.getElementById("cost"); - - if (amount instanceof HTMLElement) { - // Initialized the summary amount element - - // Writing summmary amount into the summary amount element - amount.innerText = json.amount; - } - - if (cost instanceof HTMLElement) { - // Initialized the summary cost element - - // Writing summmary cost into the summary cost element - cost.innerText = json.cost; - } - } - }); - } - }; - } - } - }) -); diff --git a/mirzaev/arming_bot/system/public/js/catalog.js b/mirzaev/arming_bot/system/public/js/catalog.js deleted file mode 100755 index bc7d58a..0000000 --- a/mirzaev/arming_bot/system/public/js/catalog.js +++ /dev/null @@ -1,1159 +0,0 @@ -"use strict"; - -// Import dependencies -import("/js/core.js").then(() => - import("/js/damper.js").then(() => - import("/js/loader.js").then(() => - import("/js/telegram.js").then(() => - import("/js/hotline.js").then(() => { - const dependencies = setInterval(() => { - if ( - typeof core === "function" && - typeof core.damper === "function" && - typeof core.telegram === "function" && - typeof core.hotline === "function" - ) { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); - - function initialization() { - if (typeof core.catalog === "undefined") { - // Not initialized - - /** - * @name Catalog - * - * @description - * Implements actions with catalog - * - * @memberof core - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich - */ - core.catalog = class catalog { - // Title - static element_title = core.main.getElementsByTagName("h2")[0]; - - // Search search - static element_search = document.getElementById("search"); - - // Search - static element_search_input = - this.element_search.getElementsByTagName("input")[0]; - - // Categories
- static element_categories = document.getElementById( - "categories", - ); - - // Filters
- static element_filters = document.getElementById("filters"); - - // Sorting
- static element_sorting = document.getElementById("sorting"); - - // Products
- static element_products = document.getElementById("products"); - - /** - * Parameters of search - * - * Will be converted to the body of the GET request ("?name=value&name2=value2") - */ - static parameters = new Map([ - [ - "text", - new URLSearchParams(document.location.search).get("text"), - ], - [ - "category", - new URLSearchParams(document.location.search).get( - "category", - ), - ], - [ - "sort", - new URLSearchParams(document.location.search).get("sort"), - ], - [ - "brand", - new URLSearchParams(document.location.search).get("brand"), - ], - ]); - - /** - * Search (interface) - * - * Request search in the catalog and render in the core.main - * - * @param {Event} event Event (keyup) - * @param {bool} force Ignore the damper? (false) - * - * @return {bool} True if an error occurs to continue the event execution - */ - static search(event, force = false) { - if (this.element_search_input instanceof HTMLElement) { - // Found the search element - - // Removing errors animations - this.element_search_input.classList.remove("error"); - - if ( - this.element_search_input.value.length < 3 && - this.element_search_input.value.lenght > 0 - ) { - // Only 1-2 character entered (this is not a search reset and is not an adequate search) - - // Exit (fail) - return true; - } - } - - if (event instanceof Event) { - // Executed from event - - if (event.type === "keyup") { - // Executed by "keyup" event - - if (event.target === this.element_search_input) { - // Executed from search element - - if (event.keyCode === 13) { - // Executed by "enter" button - - // Blocking the search element - this.element_search_input.setAttribute( - "disabled", - true, - ); - - // Execute - this.search_system(); - } else if (this.element_search_input.value.length < 3) { - // Less than 3 character entered (this is not a search reset and is not an adequate search) - - // Exit (fail) - return true; - } else { - // Executed by any else button - - // Execute under damper - this.search_damper(force); - } - } - } else { - // Executed not by "keyup" event - - // Execute under damper - this.search_damper(force); - } - - // Exit (success) - return false; - } - - // Exit (fail) - return true; - } - - /** - * Search (damper) - * - * Request search in the catalog and render in the core.main - * - * @param {bool} force Ignore the damper? (false) - * - * @return {void} - */ - static search_damper = core.damper( - (...variables) => this.search_system(...variables), - 1400, - 1, - ); - - /** - * Search (system) - * - * Request search in the catalog and render in the core.main - * - * @return {Promise} Request to the server - * - * @todo add animations of errors - */ - static async search_system() { - // @todo add timeout to unblock - - // Initialize the buffer of URN parameters - const parameters = new URLSearchParams( - window.location.search, - ); - - // Iterate parameters - this.parameters.forEach((value, parameter) => { - if ( - (typeof value === "string" || - typeof value === "number") && - !( - (parameter === "text" && value === null) || - value.length < 3 - ) - ) { - // Validated value - - // Write parameter to the buffer of URN parameters - parameters.set(parameter, value); - } else parameters.delete(parameter); - }); - - return await core.loader - .load("?" + parameters) - .then((json) => { - if (json) { - // Received response - - this.element_search_input.removeAttribute("disabled"); - // element.focus(); - - /** - * Title

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

- - if (this.element_title instanceof HTMLElement) { - // Found the title

element - - // Writing into the title

element - this.element_title.innerText = json.title; - } else { - // Not found the title

element - - // Initialize the title

element - this.element_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( - this.element_title, - first, - ); - } - } - - // Writing into the title

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

- - if (this.element_title instanceof HTMLElement) { - // Initialized the title

element - - if (json.title === "") { - // Requested to delete the title

element - - // Deleting the title

element - this.element_title.remove(); - } - } - } - - /** - * Search search - */ - if ( - typeof json.search === "string" && - json.search.length > 0 - ) { - // Received and validated the searcn HTMl-code - - if (this.element_search instanceof HTMLElement) { - // Found the search element - - // Writing into the search element - this.element_search.outerHTML = json.search; - - // Reinitializing the parameter with the search element - this.element_search = document.getElementById( - "search", - ); - } else { - // Not found the search element - - // Initialize the search element - this.element_search = document.createElement( - "search", - ); - - if (core.main instanceof HTMLElement) { - // Found the
element - - if (this.element_title instanceof HTMLElement) { - // Initialized the title

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

element in the
element - core.main.insertBefore( - this.element_search, - this.element_title.nextSibling, - ); - } 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( - this.element_search, - first, - ); - } - } - - // Writing into the search element - this.element_search.outerHTML = json.search; - - // Reinitializing the parameter with the search element - this.element_search = document.getElementById( - "search", - ); - - // Reinitializing the parameter with the search element - this.element_search_input = - this.element_search.getElementsByTagName( - "input", - )[0]; - } - } - } else { - // Not received or not validated the search HTML-code - - if (this.element_search instanceof HTMLElement) { - // Initialized the search element - - if (json.search === "") { - // Requested to delete the search element - - // Deleting the search element - this.element_search.remove(); - } - } - } - - /** - * Categories - */ - if ( - typeof json.categories === "string" && - json.categories.length > 0 - ) { - // Received and validated the categories HTMl-code - - if (this.element_categories instanceof HTMLElement) { - // Found the categories
element - - // Writing into the categories
element - this.element_categories.outerHTML = json.categories; - - // Reinitializing the parameter with the categories
element - this.element_categories = document.getElementById( - "categories", - ); - } else { - // Not found the categories
element - - // Initialize the categories
element - this.element_categories = document.createElement( - "section", - ); - - if (core.main instanceof HTMLElement) { - // Found the
element - - if (this.element_search instanceof HTMLElement) { - // Initialized the search element in the
element - - // Writing the categories
element after the search element in the
element - core.main.insertBefore( - this.element_categories, - this.element_search.nextSibling, - ); - } else if ( - this.element_title instanceof HTMLElement - ) { - // Initialized the title

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

element in the
element - core.main.insertBefore( - this.element_categories, - this.element_title.nextSibling, - ); - } 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( - this.element_categories, - first, - ); - } - } - - // Writing into the categories
element - this.element_categories.outerHTML = - json.categories; - - // Reinitializing the parameter with the categories
element - this.element_categories = document.getElementById( - "categories", - ); - } - } - } else { - // Not received or not validated the categories HTML-code - - if (this.element_categories instanceof HTMLElement) { - // Initialized the categories
element - - // Deleting the categories
element - this.element_categories.remove(); - } - } - - /** - * Filters - */ - if ( - typeof json.filters === "string" && - json.filters.length > 0 - ) { - // Received and validated the filters HTMl-code - - if (this.element_filters instanceof HTMLElement) { - // Found the filters
element - - // Writing into the filters
element - this.element_filters.outerHTML = json.filters; - - // Reinitializing the parameter with the filters
element - this.element_filters = document.getElementById( - "filters", - ); - } else { - // Not found the filters
element - - // Initialize the filters
element - this.element_filters = document.createElement( - "section", - ); - - if (core.main instanceof HTMLElement) { - // Found the
element - - if ( - this.element_categories instanceof HTMLElement - ) { - // Initialized the categories
element in the
element - - // Writing the filters
element after the categories
element in the
element - core.main.insertBefore( - this.element_filters, - this.element_categories.nextSibling, - ); - } else if ( - this.element_search instanceof HTMLElement - ) { - // Initialized the search element in the
element - - // Writing the filters
element after the search element in the
element - core.main.insertBefore( - this.element_filters, - this.element_search.nextSibling, - ); - } else if ( - this.element_title instanceof HTMLElement - ) { - // Initialized the title

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

element in the
element - core.main.insertBefore( - this.element_filters, - this.element_title.nextSibling, - ); - } 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( - this.element_filters, - first, - ); - } - } - - // Writing into the filters
element - this.element_filters.outerHTML = json.filters; - - // Reinitializing the parameter with the filters
element - this.element_filters = document.getElementById( - "filters", - ); - } - } - } else { - // Not received or not validated the filters HTML-code - - if (this.element_filters instanceof HTMLElement) { - // Initialized the filters
element - - // Deleting the filters
element - this.element_filters.remove(); - } - } - - /** - * Sorting - */ - if ( - typeof json.sorting === "string" && - json.sorting.length > 0 - ) { - // Received and validated the sorting HTMl-code - - if (this.element_sorting instanceof HTMLElement) { - // Found the sorting
element - - // Writing into the sorting
element - this.element_sorting.outerHTML = json.sorting; - - // Reinitializing the parameter with the sorting
element - this.element_sorting = document.getElementById( - "sorting", - ); - } else { - // Not found the sorting
element - - // Initialize the sorting
element - this.element_sorting = document.createElement( - "section", - ); - - if (core.main instanceof HTMLElement) { - // Found the
element - - if (this.element_filters instanceof HTMLElement) { - // Initialized the filters
element in the
element - - // Writing the sorting
element after the filters
element in the
element - core.main.insertBefore( - this.element_sorting, - this.element_filters.nextSibling, - ); - } else if ( - this.element_categories instanceof HTMLElement - ) { - // Initialized the categories
element in the
element - - // Writing the sorting
element after the categories
element in the
element - core.main.insertBefore( - this.element_sorting, - this.element_categories.nextSibling, - ); - } else if ( - this.element_search instanceof HTMLElement - ) { - // Initialized the search element in the
element - - // Writing the sorting
element after the search element in the
element - core.main.insertBefore( - this.element_sorting, - this.element_search.nextSibling, - ); - } else if ( - this.element_title instanceof HTMLElement - ) { - // Initialized the title

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

element in the
element - core.main.insertBefore( - this.element_sorting, - this.element_title.nextSibling, - ); - } 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( - this.element_sorting, - first, - ); - } - } - - // Writing into the sorting
element - this.element_sorting.outerHTML = json.sorting; - - // Reinitializing the parameter with the sorting
element - this.element_sorting = document.getElementById( - "sorting", - ); - } - } - } else { - // Not received or not validated the sorting HTML-code - - if (this.element_sorting instanceof HTMLElement) { - // Initialized the sorting
element - - // Deleting the sorting
element - this.element_sorting.remove(); - } - } - - /** - * Products - */ - if ( - typeof json.products === "string" && - json.products.length > 0 - ) { - // Received and validated the products HTMl-code - - if (this.element_products instanceof HTMLElement) { - // Found the products
element - - // Writing into the products
element - this.element_products.outerHTML = json.products; - - // Reinitializing the parameter with the products
element - this.element_products = document.getElementById( - "products", - ); - } else { - // Not found the products
element element - - // Initialize the products
element - this.element_products = document.createElement( - "section", - ); - - if (core.main instanceof HTMLElement) { - // Found the
element - - if (this.element_sorting instanceof HTMLElement) { - // Initialized the sorting
element in the
element - - // Writing the products
element after the sorting
element in the
element - core.main.insertBefore( - this.element_products, - this.element_sorting.nextSibling, - ); - } else if ( - this.element_filters instanceof HTMLElement - ) { - // Initialized the filters
element in the
element - - // Writing the products
element after the filters
element in the
element - core.main.insertBefore( - this.element_products, - this.element_filters.nextSibling, - ); - } else if ( - this.element_categories instanceof HTMLElement - ) { - // Initialized the categories
element in the
element - - // Writing the products
element after the categories
element in the
element - core.main.insertBefore( - this.element_products, - this.element_categories.nextSibling, - ); - } else if ( - this.element_search instanceof HTMLElement - ) { - // Initialized the search element in the
element - - // Writing the products
element after the search element in the
element - core.main.insertBefore( - this.element_products, - this.element_search.nextSibling, - ); - } else if ( - this.element_title instanceof HTMLElement - ) { - // Initialized the title

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

element in the
element - core.main.insertBefore( - this.element_products, - this.element_title.nextSibling, - ); - } 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( - this.element_products, - first, - ); - } - } - - // Writing into the products
element - this.element_products.outerHTML = json.products; - - // Reinitializing the parameter with the products
element - this.element_products = document.getElementById( - "products", - ); - } - } - } else { - // Not received or not validated the products HTML-code - - if (this.element_products instanceof HTMLElement) { - // Initialized the products
element - - // Deleting the products
element - this.element_products.remove(); - } - } - } - }); - } - - /** - * Product card (interface) - * - * Request product data and render in core.window - * - * @param {HTMLElement} button Button of the product - * @param {bool} force Ignore the damper? (false) - * - * @return {bool} True if an error occurs to continue the event execution - */ - static product(button, force = false) { - // Initializing identifier of the category - const identifier = button.parentElement.getAttribute( - "data-product-identifier", - ); - - // Execute under damper - this.product_damper(identifier, force); - - // Exit (success) - return false; - } - - /** - * Product card (damper) - * - * Request product data and render in core.window - * - * @param {string} identifier Identifier of the product - * @param {bool} force Ignore the damper? (false) - * - * @return {void} - */ - static product_damper = core.damper( - (...variables) => this.product_system(...variables), - 400, - 1, - ); - - /** - * Product card (system) - * - * Request product data and render in core.window - * - * @param {string} identifier Identifier of the product - * - * @return {Promise} Request to the server - */ - static async product_system(identifier) { - 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).then((json) => { - if ( - json.errors !== null && - typeof json.errors === "object" && - json.errors.length > 0 - ) { - // Fail (received errors) - } else { - // Success (not received errors) - - if (core.window instanceof HTMLElement) { - // Initialized active window - - // Delete active window - core.window.remove(); - } - - 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", "unselectable"); - - const h3 = document.createElement("h3"); - - const name = document.createElement("span"); - name.classList.add("name"); - 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.classList.add("images", "unselectable"); - - const button = core.telegram.api.isVisible; - - // блокировка закрытия изображений - let images_from; - const _images_from = (event) => (images_from = event); - images.addEventListener("mousedown", _images_from); - images.addEventListener("touchstart", _images_from); - - const _open = (event) => { - if ( - event.type === "touchstart" || - event.button === 0 - ) { - { - let x = event.pageX || event.touches[0].pageX; - let y = event.pageY || event.touches[0].pageY; - let _x = images_from.pageX || - images_from.touches[0].pageX; - let _y = images_from.pageY || - images_from.touches[0].pageY; - - if ( - _x - x < 10 && - _x - x > -10 && - _y - y < 10 && - _y - y > -10 - ) { - images.classList.add("extend"); - - if (button) { - core.telegram.api.MainButton.hide(); - } - - setTimeout(() => { - images.hotline.step = 0; - images.hotline.wheel = false; - images.hotline.start(); - images.addEventListener("mouseup", _close); - images.addEventListener("touchend", _close); - }, 300); - images.removeEventListener("mouseup", _open); - images.removeEventListener("touchend", _open); - } - } - } - }; - - const _close = (event) => { - let x = event.pageX || event.touches[0].pageX; - let y = event.pageY || event.touches[0].pageY; - let _x = images_from.pageX || - images_from.touches[0].pageX; - let _y = images_from.pageY || - images_from.touches[0].pageY; - - if ( - event.type === "touchstart" || - event.button === 0 - ) { - if ( - _x - x < 10 && - _x - x > -10 && - _y - y < 10 && - _y - y > -10 - ) { - // Курсор не был сдвинут на 10 квардратных пикселей - // (в идеале надо переделать на таймер 2 секунды есди зажата кнопка то ничего не делать а просто двигать) - // пох абсолютно сейчас заказчик слишком охуевший для этого - - images.hotline.step = -0.3; - images.hotline.wheel = true; - - if (width < card.offsetWidth) { - images.hotline.stop(); - } - images.classList.remove("extend"); - - if (button) { - core.telegram.api.MainButton.show(); - } - - images.removeEventListener("mouseup", _close); - images.removeEventListener("touchend", _close); - images.addEventListener("mousedown", _start); - images.addEventListener("touchstart", _start); - } - } - }; - - 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); - - for (const uri of json.product.images) { - const image = document.createElement("img"); - image.setAttribute("src", uri); - image.setAttribute("ondragstart", "return false;"); - - images.append(image); - } - - 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"); - cost.innerText = json.product.cost + "р"; - console.log(json.product.cost); - - h3.append(name); - exit.append(exit_icon); - h3.append(exit); - card.append(h3); - card.append(images); - header.append(brand); - card.append(header); - card.append(description); - card.append(compatibility); - footer.append(dimensions); - footer.append(weight); - footer.append(cost); - card.append(footer); - wrap.append(card); - document.body.append(wrap); - - // Reinitialize parameter - core.window = document.getElementById("window"); - - let width = 0; - let buffer; - [...images.children].forEach( - (child) => (width += child.offsetWidth + - (isNaN( - buffer = parseFloat( - getComputedStyle(child).marginRight, - ), - ) - ? 0 - : buffer)), - ); - - // блокировка закрытия карточки - 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(); - } - - 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); - }; - - 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); - - images.hotline = new core.hotline( - json.product.identfier, - images, - ); - images.hotline.step = -0.3; - images.hotline.wheel = true; - images.hotline.touch = true; - - if (width > card.offsetWidth) { - images.hotline.start(); - } - } - } - }); - } - } - }; - } - } - }) - ) - ) - ) -); diff --git a/mirzaev/arming_bot/system/public/js/connection.js b/mirzaev/arming_bot/system/public/js/connection.js deleted file mode 100755 index a15905e..0000000 --- a/mirzaev/arming_bot/system/public/js/connection.js +++ /dev/null @@ -1,324 +0,0 @@ -"use strict"; - -// Import dependencies -import("/js/core.js").then(() => - import("/js/damper.js").then(() => { - const dependencies = setInterval(() => { - if ( - typeof core === "function" && - typeof core.damper === "function" - ) { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); - - function initialization() { - if (typeof core.connection === "undefined") { - // Not initialized - - /** - * @name Connection - * - * @description - * Implements actions with websocket connection - * - * @memberof core - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich - */ - core.connection = class connection { - // Wrap of indicator of the connection - static wrap = document.getElementById("connection"); - - // Indicator of the connection - static indicator = this.wrap.getElementsByTagName("i")[0]; - - // Description of the connection - static description = this.wrap.getElementsByTagName("small")[0]; - - // Statuc of the connection - static connected = false; - - // Duration of the disconnected status - static timeout = 0; - - // Instance of the time counter in disconnected status - static counter; - - // Socket address (xn--e1ajlli это сокет) - static socket = "wss://arming.dev.mirzaev.sexy:9502"; - - // Instance of connection to the socket - static session; - - // Iterval for reconnect - static interval; - - // Attempts to connect (when core.connection.readyState === 0) - static attempts = 0; - - // Interval for block - static block; - - /** - * Initialize status of the connection to socket - * - * @param {bool} connected Connected? - * - * @return {void} - */ - static status(connected = false) { - if (this.indicator instanceof HTMLElement) { - // Initialized the indicator - - if (this.connected = connected) { - // Connected - - this.wrap.setAttribute("title", "Connected"); - - this.indicator.classList.remove("disconnected"); - this.indicator.classList.add("connected"); - - clearInterval(this.counter); - this.description.innerText = ""; - this.counter = undefined; - this.timeout = 0; - } else { - // Disconnected - - this.wrap.setAttribute("title", "Disconnected"); - - this.indicator.classList.remove("connected"); - this.indicator.classList.add("disconnected"); - - if (typeof this.counter === "undefined") { - this.counter = setInterval(() => { - this.timeout += 0.01; - this.description.innerText = this.timeout.toFixed(2); - }, 10); - } - } - } - } - - /** - * Connect to the socket - * - * @param {bool|number} interval Connection check interval (ms) - * @param {function} preprocessing Will be executed every cycle - * @param {function} onmessage New message - * @param {function} onopen Connection opened - * @param {function} onclose Connection closed - * @param {function} onerror An error has occurred - * - * @return {Promise} - */ - static connect( - interval = false, - preprocessing, - onmessage, - onopen, - onclose, - onerror, - ) { - return new Promise((resolve, reject) => { - try { - if (typeof interval === "number" && interval > 0) { - // Connect with automatic reconnect - - if (typeof this.interval === "undefined") { - this.interval = setInterval(() => { - preprocessing(); - - if ( - !(this.session instanceof WebSocket) || - (this.session.readyState === 3 || - this.session.readyState === 4) || - (this.session.readyState === 0 && - ++this.attempts > 10) - ) { - this.attempts = 0; - - if (this.session instanceof WebSocket) { - this.session.close(); - } - - this.session = new WebSocket(this.socket); - this.session.addEventListener("message", (e) => { - try { - const json = JSON.parse(e.data); - - if (json.type === "registration") { - // Подключение сокета к сессии - - fetch("/socket/registration", { - method: "POST", - headers: { - "Content-Type": - "application/x-www-form-urlencoded", - }, - body: `key=${json.key}`, - }); - } - } catch (_e) {} - }); - this.session.addEventListener("message", onmessage); - this.session.addEventListener("open", onopen); - this.session.addEventListener("close", onclose); - this.session.addEventListener("error", onerror); - - resolve(this.session); - } else resolve(this.session); - }, interval); - } - } else { - // Connect without reconnecting - - if ( - !(this.session instanceof WebSocket) || - (this.session.readyState === 3 || - this.session.readyState === 4) - ) { - if (this.session instanceof WebSocket) { - this.session.close(); - } - - this.session = new WebSocket(this.socket); - this.session.addEventListener("message", onmessage); - this.session.addEventListener("open", onopen); - this.session.addEventListener("close", onclose); - this.session.addEventListener("error", onerror); - - resolve(this.session); - } else resolve(this.session); - } - } catch (_e) {} - }); - } - - /** - * Core is connected to the socket? - * - * @return {bool} - */ - static connected() { - return this.session instanceof WebSocket && - this.session.readyState === 1; - } - }; - } - - core.connection.connect( - 3000, - () => - core.connection.status( - core.connection.session instanceof WebSocket && - core.connection.session.readyState === 1, - ), - (e) => { - try { - const json = JSON.parse(e.data); - - if (json.target === "task") { - // Заявка - - // Инициализация строки - const row = document.getElementById(json._key); - - if (row instanceof HTMLElement) { - // Инициализирована строка - - if (json.type === "blocked") { - // Заблокирована заявка - - // Запись статуса: "заблокирована" - row.setAttribute("data-blocked", json.account._key); - row.setAttribute( - "title", - "Редактирует: " + json.account.name, - ); - - // Удалить блокировку (60000 === 1 минута) (в базе данных стоит expires 1 минута тоже) - setTimeout(() => { - // Удаление статуса: "заблокирована" - row.removeAttribute("data-blocked"); - row.removeAttribute("title"); - - // Обновление строки - tasks.row(row); - }, 60000); - } else if (json.type === "unblocked") { - // Разблокирована заявка - - // Удаление статуса: "заблокирована" - row.removeAttribute("data-blocked"); - row.removeAttribute("title"); - - // Обновление строки - tasks.row(row); - } else if (json.type === "updated") { - // Обновлена заявка - - // Обновление строки - tasks.row(row); - } else if (json.type === "deleted") { - // Удалена заявка - - // Удаление строки - row.remove(); - } - } - } - } catch (_e) {} - // Инициализация идентифиатора - //const id = row.getAttribute("id"); - - // Инициализация количества непрочитанных сообщений - //const messages = row.lastElementChild.innerText; - - // Инициализация статуса активной строки - //const selected = row.getAttribute("data-selected"); - - // Реинициализация строки - //row.outerHTML = data.rows; - - // Реинициализация перезаписанной строки - //row = document.getElementById(id); - - // Копирование статуса активной строки - //if ( - // typeof selected === "string" && - // selected === "true" && - // document.body.contains(document.getElementById("popup")) - //) { - // row.setAttribute("data-selected", "true"); - //} - }, - (e) => { - //connection.status( - // core.connection instanceof WebSocket && - // core.connection.readyState === 1, - //) - //console.log("Connected to WebSocket!"); - }, - (e) => { - //connection.status( - // core.connection instanceof WebSocket && - // core.connection.readyState === 1, - //) - //console.log("Connection closed"); - }, - (e) => { - //console.log("Error happens"); - }, - ); - } - }) -); diff --git a/mirzaev/arming_bot/system/public/js/core.js b/mirzaev/arming_bot/system/public/js/core.js index f66cbd5..2c4a6ac 100755 --- a/mirzaev/arming_bot/system/public/js/core.js +++ b/mirzaev/arming_bot/system/public/js/core.js @@ -1,5 +1,3 @@ -"use strict"; - /** * @name Core * @@ -9,7 +7,7 @@ * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License * @author Arsen Mirzaev Tatyano-Muradovich */ -const core = class core { +class core { // Domain static domain = window.location.hostname; @@ -66,31 +64,51 @@ const core = class core { } /** - * Buffer + * @name Modules + * + * @method connect(modules) Connect modules + * + * @return {Array} List of initialized modules + */ + static modules() { + return Object.keys(this).filter((module) => + this[module]?.type === "module" + ); + } + + /** + * @name Buffer */ static buffer = class buffer { /** - * Write to buffers + * @name Write to buffers + * + * @description + * Write to buffers (interface) * * @param {string} name Name of the parameter * @param {string} value Value of the parameter (it can be JSON) * - * @return {void} + * @return {bool} Execution completed with an error? */ static write(name, value) { - if (typeof this.session === "function") { - // Initialized the session implement object + core.modules.connect("damper").then( + () => { + // Imported the damper module - // Write to the session buffer - core.session.buffer.write(name, value); - } + // Execute under damper + this.write.damper(name, value); + }, + () => { + // Not imported the damper module - if (typeof this.account === "function") { - // Initialized the account implement object + // Execute + this.write.system(name, value); + }, + ); - // Write to the account buffer - core.account.buffer.write(name, value); - } + // Exit (success) + return false; } }; @@ -274,4 +292,112 @@ const core = class core { }, 300, ); */ -}; +} + +Object.assign( + core.modules, + { + /** + * @name Connect modules + * + * @param {Array|string} modules Names of modules or name of the module + * + * @return {Prommise} + */ + async connect(modules) { + // Normalisation required argiments + if (typeof modules === "string") modules = [modules]; + + if (modules instanceof Array) { + // Received and validated required arguments + + // Initializing the registry of loaded modules + const loaded = []; + + for (const module of modules) { + // Iterating over modules + + // Downloading, importing and writing the module into a core property and into registry of loaded modules + core[module] = + loaded[module] = + await (await import(`./modules/${module}.js`)).default; + } + + // Exit (success) + return loaded; + } + }, + }, +); + +core.modules.connect("damper").then(() => { + // Imported the damper module + + Object.assign( + core.buffer.write, + { + /** + * @name Write to buffers + * + * @description + * Write to buffers (damper) + * + * @param {string} name Name of the parameter + * @param {string} value Value of the parameter (it can be JSON) + * @param {bool} force Ignore the damper? (false) + * + * @return {Promise} + */ + damper: core.damper( + (...variables) => core.buffer.write.system(...variables), + 300, + 3, + ), + }, + ); +}); + +/** + * @name Write to buffers + * + * @description + * Write to buffers (system) + * + * @param {string} name Name of the parameter + * @param {string} value Value of the parameter (it can be JSON) + * + * @return {Promise} + */ +Object.assign( + core.buffer.write, + { + system( + name, + value, + resolve = () => {}, + reject = () => {}, + ) { + try { + core.modules.connect("session").then(() => { + // Imported the session module + + // Write to the session buffer + session.default.buffer?.write(name, value); + }); + + core.modules.connect("account").then(() => { + // Imported the account module + + // Write to the account buffer + account.default.buffer?.write(name, value); + }); + + // Exit (success) + resolve(); + } catch (e) { + // Exit (fail) + reject(e); + } + }, + }, +); diff --git a/mirzaev/arming_bot/system/public/js/damper.js b/mirzaev/arming_bot/system/public/js/damper.js deleted file mode 100755 index 7bf4107..0000000 --- a/mirzaev/arming_bot/system/public/js/damper.js +++ /dev/null @@ -1,96 +0,0 @@ -"use strict"; - -// Import dependencies -import("/js/core.js").then(() => { - const dependencies = setInterval(() => { - if (typeof core === "function") { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); - - function initialization() { - if (typeof core.damper === "undefined") { - // Not initialized - - /** - * @name Damper - * - * @description - * Execute multiple "function" calls in a "timeout" amount of time just once - * - * @param {function} function Function to execute after damping - * @param {number} timeout Timer in milliseconds (ms) - * @param {number} force Argument number storing the status of enforcement execution (see @example) - * - * @return {Promise} - * - * @memberof core - * - * @example - * a = damper( - * async ( - * a, // 0 - * b, // 1 - * c, // 2 - * force = false, // 3 - * d, // 4 - * resolve, - * reject - * ) => { - * // Body of the function - * - * resolve(); - * }, - * 500, - * 3, // 3 -> "force" argument - * ); - * - * a('for a', 'for b', 'for c', true, 'for d'); // Force execute is enabled - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich - */ - core.damper = (func, timeout = 300, force) => { - // Declaring of the timer for executing the function - let timer; - - return ((...args) => { - return new Promise((resolve, reject) => { - // Deinitializing of the timer - clearTimeout(timer); - - if (typeof force === "number" && args[force]) { - // Requested execution with ignoring the timer - - // Deleting the force argument - if (typeof force === "number") delete args[force - 1]; - - // Writing promise handlers into the arguments variable - args.push(resolve, reject); - - // Executing the function - func.apply(this, args); - } else { - // Normal execution - - // Deleting the force argument - if (typeof force === "number") delete args[force - 1]; - - // Writing promise handlers into the arguments variable - args.push(resolve, reject); - - // Resetting the timer and executing the function when the timer expires - timer = setTimeout(() => func.apply(this, args), timeout); - } - }); - }); - }; - } - } -}); diff --git a/mirzaev/arming_bot/system/public/js/hotline.js b/mirzaev/arming_bot/system/public/js/hotline.js index 3fb2af1..8b7adcc 100644 --- a/mirzaev/arming_bot/system/public/js/hotline.js +++ b/mirzaev/arming_bot/system/public/js/hotline.js @@ -1,777 +1,750 @@ "use strict"; -// Import dependencies -import("/js/core.js").then(() => { - const dependencies = setInterval(() => { - if (typeof core === "function") { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); +/** + * @name Бегущая строка + * + * @description + * Простой, но мощный класс для создания бегущих строк. Поддерживает + * перемещение мышью и прокрутку колесом, полностью настраивается очень гибок + * для настроек в CSS и подразумевается, что отлично индексируется поисковыми роботами. + * Имеет свой препроцессор, благодаря которому можно создавать бегущие строки + * без программирования - с помощью HTML-аттрибутов, а так же возможность + * изменять параметры (data-hotline-* аттрибуты) на лету. Есть возможность вызывать + * события при выбранных действиях для того, чтобы пользователь имел возможность + * дорабатывать функционал без изучения и изменения моего кода + * + * @example + * сonst hotline = new hotline(); + * hotline.step = '-5'; + * hotline.start(); + * + * @todo + * 1. Бесконечный режим - элементы не удаляются если видны на экране (будут дубликаты). + * Сейчас при БЫСТРОМ прокручивании можно заметит как элементы "появляются" в начале и конце строки. + * 2. "gap" and "padding" in wrap should be removed! or added here to the calculations + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich + */ +export default class hotline { + // Идентификатор + #id = 0; - function initialization() { - if (typeof core.hotline === "undefined") { - // Not initialized + // Оболочка (instanceof HTMLElement) + #shell = document.getElementById("hotline"); - /** - * @name Бегущая строка - * - * @description - * Простой, но мощный класс для создания бегущих строк. Поддерживает - * перемещение мышью и прокрутку колесом, полностью настраивается очень гибок - * для настроек в CSS и подразумевается, что отлично индексируется поисковыми роботами. - * Имеет свой препроцессор, благодаря которому можно создавать бегущие строки - * без программирования - с помощью HTML-аттрибутов, а так же возможность - * изменять параметры (data-hotline-* аттрибуты) на лету. Есть возможность вызывать - * события при выбранных действиях для того, чтобы пользователь имел возможность - * дорабатывать функционал без изучения и изменения моего кода - * - * @memberof core - * - * @example - * сonst hotline = new hotline(); - * hotline.step = '-5'; - * hotline.start(); - * - * @todo - * 1. Бесконечный режим - элементы не удаляются если видны на экране (будут дубликаты). - * Сейчас при БЫСТРОМ прокручивании можно заметит как элементы "появляются" в начале и конце строки. - * 2. "gap" and "padding" in wrap should be removed! or added here to the calculations - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich - */ - core.hotline = class hotline { - // Идентификатор - #id = 0; + // Инстанция горячей строки + #instance = null; - // Оболочка (instanceof HTMLElement) - #shell = document.getElementById("hotline"); + // Перемещение + #transfer = true; - // Инстанция горячей строки - #instance = null; + // Движение + #move = true; - // Перемещение - #transfer = true; + // Наблюдатель + #observer = null; - // Движение - #move = true; + // Реестр запрещённых к изменению параметров + #block = new Set(["events"]); - // Наблюдатель - #observer = null; + // Status (null, active, inactive) + #status = null; - // Реестр запрещённых к изменению параметров - #block = new Set(["events"]); + // Настраиваемые параметры + transfer = null; + move = null; + delay = 10; + step = 1; + hover = true; + movable = true; + sticky = false; + wheel = false; + delta = null; + vertical = false; + button = 0; // button for grabbing. 0 is main mouse button (left) + observe = false; + events = new Map([ + ["start", false], + ["stop", false], + ["move", false], + ["move.block", false], + ["move.unblock", false], + ["offset", false], + ["transfer.start", true], + ["transfer.end", true], + ["mousemove", false], + ["touchmove", false], + ]); - // Status (null, active, inactive) - #status = null; + // Is hotline currently moving due to "onmousemove" or "ontouchmove"? + moving = false; - // Настраиваемые параметры - transfer = null; - move = null; - delay = 10; - step = 1; - hover = true; - movable = true; - sticky = false; - wheel = false; - delta = null; - vertical = false; - button = 0; // button for grabbing. 0 is main mouse button (left) - observe = false; - events = new Map([ - ["start", false], - ["stop", false], - ["move", false], - ["move.block", false], - ["move.unblock", false], - ["offset", false], - ["transfer.start", true], - ["transfer.end", true], - ["mousemove", false], - ["touchmove", false], - ]); + constructor(id, shell) { + // Запись идентификатора + if (typeof id === "string" || typeof id === "number") this.#id = id; - // Is hotline currently moving due to "onmousemove" or "ontouchmove"? - moving = false; + // Запись оболочки + if (shell instanceof HTMLElement) this.#shell = shell; + } - constructor(id, shell) { - // Запись идентификатора - if (typeof id === "string" || typeof id === "number") this.#id = id; + start() { + if (this.#instance === null) { + // Нет запущенной инстанции бегущей строки - // Запись оболочки - if (shell instanceof HTMLElement) this.#shell = shell; + // Инициализация ссылки на ядро + const _this = this; + + // Запуск движения + this.#instance = setInterval(function () { + if (_this.#shell.childElementCount > 1) { + // Найдено содержимое бегущей строки (2 и более) + + // Инициализация буфера для временных данных + let buffer; + + // Инициализация данных первого элемента в строке + const first = { + element: (buffer = _this.#shell.firstElementChild), + coords: buffer.getBoundingClientRect(), + }; + + if (_this.vertical) { + // Вертикальная бегущая строка + + // Инициализация сдвига у первого элемента (движение) + first.offset = isNaN( + buffer = parseFloat(first.element.style.marginTop), + ) + ? 0 + : buffer; + + // Инициализация отступа до второго элемента у первого элемента (разделение) + first.separator = isNaN( + buffer = parseFloat( + getComputedStyle(first.element).marginBottom, + ), + ) + ? 0 + : buffer; + + // Инициализация крайнего с конца ребра первого элемента в строке + first.end = first.coords.y + first.coords.height + + first.separator; + } else { + // Горизонтальная бегущая строка + + // Инициализация отступа у первого элемента (движение) + first.offset = isNaN( + buffer = parseFloat(first.element.style.marginLeft), + ) + ? 0 + : buffer; + + // Инициализация отступа до второго элемента у первого элемента (разделение) + first.separator = isNaN( + buffer = parseFloat( + getComputedStyle(first.element).marginRight, + ), + ) + ? 0 + : buffer; + + // Инициализация крайнего с конца ребра первого элемента в строке + first.end = first.coords.x + first.coords.width + + first.separator; + } + + if ( + (_this.vertical && + Math.round(first.end) < _this.#shell.offsetTop) || + (!_this.vertical && + Math.round(first.end) < _this.#shell.offsetLeft) + ) { + // Элемент (вместе с отступом до второго элемента) вышел из области видимости (строки) + + if ( + (_this.transfer === null && _this.#transfer) || + _this.transfer === true + ) { + // Перенос разрешен + + if (_this.vertical) { + // Вертикальная бегущая строка + + // Удаление отступов (движения) + first.element.style.marginTop = null; + } else { + // Горизонтальная бегущая строка + + // Удаление отступов (движения) + first.element.style.marginLeft = null; + } + + // Копирование первого элемента в конец строки + _this.#shell.appendChild(first.element); + + if (_this.events.get("transfer.end")) { + // Запрошен вызов события: "перемещение в конец" + + // Вызов события: "перемещение в конец" + document.dispatchEvent( + new CustomEvent(`hotline.${_this.#id}.transfer.end`, { + detail: { + element: first.element, + offset: -( + (_this.vertical + ? first.coords.height + : first.coords.width) + first.separator + ), + }, + }), + ); + } + } + } else if ( + (_this.vertical && + Math.round(first.coords.y) > _this.#shell.offsetTop) || + (!_this.vertical && + Math.round(first.coords.x) > _this.#shell.offsetLeft) + ) { + // Передняя (движущая) граница первого элемента вышла из области видимости + + if ( + (_this.transfer === null && _this.#transfer) || + _this.transfer === true + ) { + // Перенос разрешен + + // Инициализация отступа у последнего элемента (разделение) + const separator = (buffer = isNaN( + buffer = parseFloat( + getComputedStyle(_this.#shell.lastElementChild)[ + _this.vertical ? "marginBottom" : "marginRight" + ], + ), + ) + ? 0 + : buffer) === 0 + ? first.separator + : buffer; + + // Инициализация координат первого элемента в строке + const coords = _this.#shell.lastElementChild + .getBoundingClientRect(); + + if (_this.vertical) { + // Вертикальная бегущая строка + + // Удаление отступов (движения) + _this.#shell.lastElementChild.style.marginTop = -coords.height - + separator + "px"; + } else { + // Горизонтальная бегущая строка + + // Удаление отступов (движения) + _this.#shell.lastElementChild.style.marginLeft = -coords.width - + separator + "px"; + } + + // Копирование последнего элемента в начало строки + _this.#shell.insertBefore( + _this.#shell.lastElementChild, + first.element, + ); + + // Удаление отступов у второго элемента в строке (движения) + _this.#shell.children[1].style[ + _this.vertical ? "marginTop" : "marginLeft" + ] = null; + + if (_this.events.get("transfer.start")) { + // Запрошен вызов события: "перемещение в начало" + + // Вызов события: "перемещение в начало" + document.dispatchEvent( + new CustomEvent(`hotline.${_this.#id}.transfer.start`, { + detail: { + element: _this.#shell.lastElementChild, + offset: (_this.vertical ? coords.height : coords.width) + + separator, + }, + }), + ); + } + } + } else { + // Элемент в области видимости + + if ( + (_this.move === null && _this.#move) || _this.move === true + ) { + // Движение разрешено + + // Запись новых координат сдвига + const offset = first.offset + _this.step; + + // Запись сдвига (движение) + _this.offset(offset); + + if (_this.events.get("move")) { + // Запрошен вызов события: "движение" + + // Вызов события: "движение" + document.dispatchEvent( + new CustomEvent(`hotline.${_this.#id}.move`, { + detail: { + from: first.offset, + to: offset, + }, + }), + ); + } + } + } } + }, _this.delay); - start() { - if (this.#instance === null) { - // Нет запущенной инстанции бегущей строки + if (this.hover) { + // Запрошена возможность останавливать бегущую строку - // Инициализация ссылки на ядро - const _this = this; + // Инициализация сдвига + let offset = 0; - // Запуск движения - this.#instance = setInterval(function () { - if (_this.#shell.childElementCount > 1) { - // Найдено содержимое бегущей строки (2 и более) + // Инициализация слушателя события при перемещении элемента в бегущей строке + const listener = function (e) { + // Увеличение сдвига + offset += e.detail.offset ?? 0; + }; - // Инициализация буфера для временных данных - let buffer; + // Объявление переменной в области видимости обработки остановки бегущей строки + let move; - // Инициализация данных первого элемента в строке - const first = { - element: (buffer = _this.#shell.firstElementChild), - coords: buffer.getBoundingClientRect(), - }; + // Инициализация обработчика наведения курсора (остановка движения) + this.#shell.onmouseover = function (e) { + // Курсор наведён на бегущую строку - if (_this.vertical) { - // Вертикальная бегущая строка + // Блокировка движения + _this.#move = false; - // Инициализация сдвига у первого элемента (движение) - first.offset = isNaN( - buffer = parseFloat(first.element.style.marginTop), - ) - ? 0 - : buffer; + if (_this.events.get("move.block")) { + // Запрошен вызов события: "блокировка движения" - // Инициализация отступа до второго элемента у первого элемента (разделение) - first.separator = isNaN( - buffer = parseFloat( - getComputedStyle(first.element).marginBottom, - ), - ) - ? 0 - : buffer; + // Вызов события: "блокировка движения" + document.dispatchEvent( + new CustomEvent(`hotline.${_this.#id}.move.block`), + ); + } + }; - // Инициализация крайнего с конца ребра первого элемента в строке - first.end = first.coords.y + first.coords.height + - first.separator; - } else { - // Горизонтальная бегущая строка + if (this.movable) { + // Запрошена возможность двигать бегущую строку - // Инициализация отступа у первого элемента (движение) - first.offset = isNaN( - buffer = parseFloat(first.element.style.marginLeft), - ) - ? 0 - : buffer; - - // Инициализация отступа до второго элемента у первого элемента (разделение) - first.separator = isNaN( - buffer = parseFloat( - getComputedStyle(first.element).marginRight, - ), - ) - ? 0 - : buffer; - - // Инициализация крайнего с конца ребра первого элемента в строке - first.end = first.coords.x + first.coords.width + - first.separator; - } + _this.#shell.onmousedown = + _this.#shell.ontouchstart = + function ( + start, + ) { + // Handling a "mousedown" and a "touchstart" on hotline if ( - (_this.vertical && - Math.round(first.end) < _this.#shell.offsetTop) || - (!_this.vertical && - Math.round(first.end) < _this.#shell.offsetLeft) + start.type === "touchstart" || + start.button === _this.button ) { - // Элемент (вместе с отступом до второго элемента) вышел из области видимости (строки) + const x = start.pageX || start.touches[0].pageX; + const y = start.pageY || start.touches[0].pageY; - if ( - (_this.transfer === null && _this.#transfer) || - _this.transfer === true - ) { - // Перенос разрешен + // Блокировка движения + _this.#move = false; - if (_this.vertical) { - // Вертикальная бегущая строка + if (_this.events.get("move.block")) { + // Запрошен вызов события: "блокировка движения" - // Удаление отступов (движения) - first.element.style.marginTop = null; - } else { - // Горизонтальная бегущая строка - - // Удаление отступов (движения) - first.element.style.marginLeft = null; - } - - // Копирование первого элемента в конец строки - _this.#shell.appendChild(first.element); - - if (_this.events.get("transfer.end")) { - // Запрошен вызов события: "перемещение в конец" - - // Вызов события: "перемещение в конец" - document.dispatchEvent( - new CustomEvent(`hotline.${_this.#id}.transfer.end`, { - detail: { - element: first.element, - offset: -( - (_this.vertical - ? first.coords.height - : first.coords.width) + first.separator - ), - }, - }), - ); - } - } - } else if ( - (_this.vertical && - Math.round(first.coords.y) > _this.#shell.offsetTop) || - (!_this.vertical && - Math.round(first.coords.x) > _this.#shell.offsetLeft) - ) { - // Передняя (движущая) граница первого элемента вышла из области видимости - - if ( - (_this.transfer === null && _this.#transfer) || - _this.transfer === true - ) { - // Перенос разрешен - - // Инициализация отступа у последнего элемента (разделение) - const separator = (buffer = isNaN( - buffer = parseFloat( - getComputedStyle(_this.#shell.lastElementChild)[ - _this.vertical ? "marginBottom" : "marginRight" - ], - ), - ) - ? 0 - : buffer) === 0 - ? first.separator - : buffer; - - // Инициализация координат первого элемента в строке - const coords = _this.#shell.lastElementChild - .getBoundingClientRect(); - - if (_this.vertical) { - // Вертикальная бегущая строка - - // Удаление отступов (движения) - _this.#shell.lastElementChild.style.marginTop = - -coords.height - separator + "px"; - } else { - // Горизонтальная бегущая строка - - // Удаление отступов (движения) - _this.#shell.lastElementChild.style.marginLeft = - -coords.width - separator + "px"; - } - - // Копирование последнего элемента в начало строки - _this.#shell.insertBefore( - _this.#shell.lastElementChild, - first.element, - ); - - // Удаление отступов у второго элемента в строке (движения) - _this.#shell.children[1].style[ - _this.vertical ? "marginTop" : "marginLeft" - ] = null; - - if (_this.events.get("transfer.start")) { - // Запрошен вызов события: "перемещение в начало" - - // Вызов события: "перемещение в начало" - document.dispatchEvent( - new CustomEvent(`hotline.${_this.#id}.transfer.start`, { - detail: { - element: _this.#shell.lastElementChild, - offset: - (_this.vertical ? coords.height : coords.width) + - separator, - }, - }), - ); - } - } - } else { - // Элемент в области видимости - - if ( - (_this.move === null && _this.#move) || _this.move === true - ) { - // Движение разрешено - - // Запись новых координат сдвига - const offset = first.offset + _this.step; - - // Запись сдвига (движение) - _this.offset(offset); - - if (_this.events.get("move")) { - // Запрошен вызов события: "движение" - - // Вызов события: "движение" - document.dispatchEvent( - new CustomEvent(`hotline.${_this.#id}.move`, { - detail: { - from: first.offset, - to: offset, - }, - }), - ); - } - } - } - } - }, _this.delay); - - if (this.hover) { - // Запрошена возможность останавливать бегущую строку - - // Инициализация сдвига - let offset = 0; - - // Инициализация слушателя события при перемещении элемента в бегущей строке - const listener = function (e) { - // Увеличение сдвига - offset += e.detail.offset ?? 0; - }; - - // Объявление переменной в области видимости обработки остановки бегущей строки - let move; - - // Инициализация обработчика наведения курсора (остановка движения) - this.#shell.onmouseover = function (e) { - // Курсор наведён на бегущую строку - - // Блокировка движения - _this.#move = false; - - if (_this.events.get("move.block")) { - // Запрошен вызов события: "блокировка движения" - - // Вызов события: "блокировка движения" - document.dispatchEvent( - new CustomEvent(`hotline.${_this.#id}.move.block`), - ); - } - }; - - if (this.movable) { - // Запрошена возможность двигать бегущую строку - - _this.#shell.onmousedown = - _this.#shell.ontouchstart = - function ( - start, - ) { - // Handling a "mousedown" and a "touchstart" on hotline - - if ( - start.type === "touchstart" || - start.button === _this.button - ) { - const x = start.pageX || start.touches[0].pageX; - const y = start.pageY || start.touches[0].pageY; - - // Блокировка движения - _this.#move = false; - - if (_this.events.get("move.block")) { - // Запрошен вызов события: "блокировка движения" - - // Вызов события: "блокировка движения" - document.dispatchEvent( - new CustomEvent(`hotline.${_this.#id}.move.block`), - ); - } - - // Инициализация слушателей события перемещения элемента в бегущей строке - document.addEventListener( - `hotline.${_this.#id}.transfer.start`, - listener, - ); - document.addEventListener( - `hotline.${_this.#id}.transfer.end`, - listener, - ); - - // Инициализация буфера для временных данных - let buffer; - - // Инициализация данных первого элемента в строке - const first = { - offset: isNaN( - buffer = parseFloat( - _this.vertical - ? _this.#shell.firstElementChild.style - .marginTop - : _this.#shell.firstElementChild.style - .marginLeft, - ), - ) - ? 0 - : buffer, - }; - - move = (move) => { - // Обработка движения курсора - - if (_this.#status === "active") { - // Запись статуса ручного перемещения - _this.moving = true; - - const _x = move.pageX || move.touches[0].pageX; - const _y = move.pageY || move.touches[0].pageY; - - if (_this.vertical) { - // Вертикальная бегущая строка - - // Инициализация буфера местоположения - const from = - _this.#shell.firstElementChild.style.marginTop; - const to = _y - (y + offset - first.offset); - - // Движение - _this.#shell.firstElementChild.style.marginTop = - to + - "px"; - } else { - // Горизонтальная бегущая строка - - // Инициализация буфера местоположения - const from = - _this.#shell.firstElementChild.style.marginLeft; - const to = _x - (x + offset - first.offset); - - // Движение - _this.#shell.firstElementChild.style.marginLeft = - to + - "px"; - } - - if (_this.events.get(move.type)) { - // Запрошен вызов события: "перемещение" (мышью или касанием) - - // Вызов события: "перемещение" (мышью или касанием) - document.dispatchEvent( - new CustomEvent( - `hotline.${_this.#id}.${move.type}`, - { - detail: { from, to }, - }, - ), - ); - } - - // Запись курсора - _this.#shell.style.cursor = "grabbing"; - } - }; - - // Запуск обработки движения - document.addEventListener("mousemove", move); - document.addEventListener("touchmove", move); - } - }; - - // Перещапись событий браузера (чтобы не дёргалось) - _this.#shell.ondragstart = null; - - _this.#shell.onmouseup = _this.#shell.ontouchend = function () { - // Курсор деактивирован - - // Запись статуса ручного перемещения - _this.moving = false; - - // Остановка обработки движения - document.removeEventListener("mousemove", move); - document.removeEventListener("touchmove", move); - - // Сброс сдвига - offset = 0; - - document.removeEventListener( - `hotline.${_this.#id}.transfer.start`, - listener, - ); - document.removeEventListener( - `hotline.${_this.#id}.transfer.end`, - listener, - ); - - // Разблокировка движения - _this.#move = true; - - if (_this.events.get("move.unblock")) { - // Запрошен вызов события: "разблокировка движения" - - // Вызов события: "разблокировка движения" + // Вызов события: "блокировка движения" document.dispatchEvent( - new CustomEvent(`hotline.${_this.#id}.move.unblock`), + new CustomEvent(`hotline.${_this.#id}.move.block`), ); } - // Восстановление курсора - _this.#shell.style.cursor = null; - }; - } - - // Инициализация обработчика отведения курсора (остановка движения) - this.#shell.onmouseleave = function (onmouseleave) { - // Курсор отведён от бегущей строки - - if (!_this.sticky) { - // Отключено прилипание - - // Запись статуса ручного перемещения - _this.moving = false; - - // Остановка обработки движения - document.removeEventListener("mousemove", move); - document.removeEventListener("touchmove", move); - - document.removeEventListener( + // Инициализация слушателей события перемещения элемента в бегущей строке + document.addEventListener( `hotline.${_this.#id}.transfer.start`, listener, ); - document.removeEventListener( + document.addEventListener( `hotline.${_this.#id}.transfer.end`, listener, ); - // Восстановление курсора - _this.#shell.style.cursor = null; - } + // Инициализация буфера для временных данных + let buffer; - // Сброс сдвига - offset = 0; + // Инициализация данных первого элемента в строке + const first = { + offset: isNaN( + buffer = parseFloat( + _this.vertical + ? _this.#shell.firstElementChild.style + .marginTop + : _this.#shell.firstElementChild.style + .marginLeft, + ), + ) + ? 0 + : buffer, + }; - // Разблокировка движения - _this.#move = true; + move = (move) => { + // Обработка движения курсора - if (_this.events.get("move.unblock")) { - // Запрошен вызов события: "разблокировка движения" + if (_this.#status === "active") { + // Запись статуса ручного перемещения + _this.moving = true; - // Вызов события: "разблокировка движения" - document.dispatchEvent( - new CustomEvent(`hotline.${_this.#id}.move.unblock`), - ); + const _x = move.pageX || move.touches[0].pageX; + const _y = move.pageY || move.touches[0].pageY; + + if (_this.vertical) { + // Вертикальная бегущая строка + + // Инициализация буфера местоположения + const from = + _this.#shell.firstElementChild.style.marginTop; + const to = _y - (y + offset - first.offset); + + // Движение + _this.#shell.firstElementChild.style.marginTop = to + + "px"; + } else { + // Горизонтальная бегущая строка + + // Инициализация буфера местоположения + const from = + _this.#shell.firstElementChild.style.marginLeft; + const to = _x - (x + offset - first.offset); + + // Движение + _this.#shell.firstElementChild.style.marginLeft = to + + "px"; + } + + if (_this.events.get(move.type)) { + // Запрошен вызов события: "перемещение" (мышью или касанием) + + // Вызов события: "перемещение" (мышью или касанием) + document.dispatchEvent( + new CustomEvent( + `hotline.${_this.#id}.${move.type}`, + { + detail: { from, to }, + }, + ), + ); + } + + // Запись курсора + _this.#shell.style.cursor = "grabbing"; + } + }; + + // Запуск обработки движения + document.addEventListener("mousemove", move); + document.addEventListener("touchmove", move); } }; + + // Перещапись событий браузера (чтобы не дёргалось) + _this.#shell.ondragstart = null; + + _this.#shell.onmouseup = _this.#shell.ontouchend = function () { + // Курсор деактивирован + + // Запись статуса ручного перемещения + _this.moving = false; + + // Остановка обработки движения + document.removeEventListener("mousemove", move); + document.removeEventListener("touchmove", move); + + // Сброс сдвига + offset = 0; + + document.removeEventListener( + `hotline.${_this.#id}.transfer.start`, + listener, + ); + document.removeEventListener( + `hotline.${_this.#id}.transfer.end`, + listener, + ); + + // Разблокировка движения + _this.#move = true; + + if (_this.events.get("move.unblock")) { + // Запрошен вызов события: "разблокировка движения" + + // Вызов события: "разблокировка движения" + document.dispatchEvent( + new CustomEvent(`hotline.${_this.#id}.move.unblock`), + ); } - if (this.wheel) { - // Запрошена возможность прокручивать колесом мыши + // Восстановление курсора + _this.#shell.style.cursor = null; + }; + } - // Инициализация обработчика наведения курсора (остановка движения) - this.#shell.onwheel = function (e) { - // Курсор наведён на бегущую + // Инициализация обработчика отведения курсора (остановка движения) + this.#shell.onmouseleave = function (onmouseleave) { + // Курсор отведён от бегущей строки - // Инициализация буфера для временных данных - let buffer; + if (!_this.sticky) { + // Отключено прилипание - // Перемещение - _this.offset( - (isNaN( - buffer = parseFloat( - _this.#shell.firstElementChild.style[ - _this.vertical ? "marginTop" : "marginLeft" - ], - ), - ) - ? 0 - : buffer) + - (_this.delta === null - ? e.wheelDelta - : e.wheelDelta > 0 - ? _this.delta - : -_this.delta), - ); - }; - } + // Запись статуса ручного перемещения + _this.moving = false; - this.#status = "active"; + // Остановка обработки движения + document.removeEventListener("mousemove", move); + document.removeEventListener("touchmove", move); + + document.removeEventListener( + `hotline.${_this.#id}.transfer.start`, + listener, + ); + document.removeEventListener( + `hotline.${_this.#id}.transfer.end`, + listener, + ); + + // Восстановление курсора + _this.#shell.style.cursor = null; } - if (this.observe) { - // Запрошено наблюдение за изменениями аттрибутов элемента бегущей строки + // Сброс сдвига + offset = 0; - if (this.#observer === null) { - // Отсутствует наблюдатель + // Разблокировка движения + _this.#move = true; - // Инициализация ссылки на ядро - const _this = this; + if (_this.events.get("move.unblock")) { + // Запрошен вызов события: "разблокировка движения" - // Инициализация наблюдателя - this.#observer = new MutationObserver(function (mutations) { - for (const mutation of mutations) { - if (mutation.type === "attributes") { - // Запись параметра в инстанцию бегущей строки - _this.configure(mutation.attributeName); - } - } - - // Перезапуск бегущей строки - _this.restart(); - }); - - // Активация наблюдения - this.#observer.observe(this.#shell, { - attributes: true, - }); - } - } else if (this.#observer instanceof MutationObserver) { - // Запрошено отключение наблюдения - - // Деактивация наблюдения - this.#observer.disconnect(); - - // Удаление наблюдателя - this.#observer = null; - } - - if (this.events.get("start")) { - // Запрошен вызов события: "запуск" - - // Вызов события: "запуск" + // Вызов события: "разблокировка движения" document.dispatchEvent( - new CustomEvent(`hotline.${this.#id}.start`), + new CustomEvent(`hotline.${_this.#id}.move.unblock`), ); } + }; + } - return this; - } + if (this.wheel) { + // Запрошена возможность прокручивать колесом мыши - stop() { - this.#status = "inactive"; + // Инициализация обработчика наведения курсора (остановка движения) + this.#shell.onwheel = function (e) { + // Курсор наведён на бегущую - // Остановка бегущей строки - clearInterval(this.#instance); + // Инициализация буфера для временных данных + let buffer; - // Удаление инстанции интервала - this.#instance = null; + // Перемещение + _this.offset( + (isNaN( + buffer = parseFloat( + _this.#shell.firstElementChild.style[ + _this.vertical ? "marginTop" : "marginLeft" + ], + ), + ) + ? 0 + : buffer) + + (_this.delta === null + ? e.wheelDelta + : e.wheelDelta > 0 + ? _this.delta + : -_this.delta), + ); + }; + } - if (this.events.get("stop")) { - // Запрошен вызов события: "остановка" + this.#status = "active"; + } - // Вызов события: "остановка" - document.dispatchEvent(new CustomEvent(`hotline.${this.#id}.stop`)); - } + if (this.observe) { + // Запрошено наблюдение за изменениями аттрибутов элемента бегущей строки - return this; - } + if (this.#observer === null) { + // Отсутствует наблюдатель - restart() { - // Остановка бегущей строки - this.stop(); + // Инициализация ссылки на ядро + const _this = this; - // Запуск бегущей строки - this.start(); - } - - configure(attribute) { - // Инициализация названия параметра - const parameter = - (/^data-hotline-(\w+)$/.exec(attribute) ?? [, null])[1]; - - if (typeof parameter === "string") { - // Параметр найден - - // Проверка на разрешение изменения - if (this.#block.has(parameter)) return; - - // Инициализация значения параметра - const value = this.#shell.getAttribute(attribute); - - if (typeof value !== undefined || typeof value !== null) { - // Найдено значение - - // Инициализация буфера для временных данных - let buffer; - - // Запись параметра - this[parameter] = isNaN(buffer = parseFloat(value)) - ? value === "true" ? true : value === "false" ? false : value - : buffer; + // Инициализация наблюдателя + this.#observer = new MutationObserver(function (mutations) { + for (const mutation of mutations) { + if (mutation.type === "attributes") { + // Запись параметра в инстанцию бегущей строки + _this.configure(mutation.attributeName); } } - return this; + // Перезапуск бегущей строки + _this.restart(); + }); + + // Активация наблюдения + this.#observer.observe(this.#shell, { + attributes: true, + }); + } + } else if (this.#observer instanceof MutationObserver) { + // Запрошено отключение наблюдения + + // Деактивация наблюдения + this.#observer.disconnect(); + + // Удаление наблюдателя + this.#observer = null; + } + + if (this.events.get("start")) { + // Запрошен вызов события: "запуск" + + // Вызов события: "запуск" + document.dispatchEvent( + new CustomEvent(`hotline.${this.#id}.start`), + ); + } + + return this; + } + + stop() { + this.#status = "inactive"; + + // Остановка бегущей строки + clearInterval(this.#instance); + + // Удаление инстанции интервала + this.#instance = null; + + if (this.events.get("stop")) { + // Запрошен вызов события: "остановка" + + // Вызов события: "остановка" + document.dispatchEvent(new CustomEvent(`hotline.${this.#id}.stop`)); + } + + return this; + } + + restart() { + // Остановка бегущей строки + this.stop(); + + // Запуск бегущей строки + this.start(); + } + + configure(attribute) { + // Инициализация названия параметра + const parameter = (/^data-hotline-(\w+)$/.exec(attribute) ?? [, null])[1]; + + if (typeof parameter === "string") { + // Параметр найден + + // Проверка на разрешение изменения + if (this.#block.has(parameter)) return; + + // Инициализация значения параметра + const value = this.#shell.getAttribute(attribute); + + if (typeof value !== undefined || typeof value !== null) { + // Найдено значение + + // Инициализация буфера для временных данных + let buffer; + + // Запись параметра + this[parameter] = isNaN(buffer = parseFloat(value)) + ? value === "true" ? true : value === "false" ? false : value + : buffer; + } + } + + return this; + } + + offset(value) { + // Запись отступа + this.#shell.firstElementChild.style[ + this.vertical ? "marginTop" : "marginLeft" + ] = value + "px"; + + if (this.events.get("offset")) { + // Запрошен вызов события: "сдвиг" + + // Вызов события: "сдвиг" + document.dispatchEvent( + new CustomEvent(`hotline.${this.#id}.offset`, { + detail: { + to: value, + }, + }), + ); + } + + return this; + } + + static preprocessing(event = false) { + // Инициализация счётчиков инстанций горячей строки + const success = new Set(); + let error = 0; + + for ( + const element of document.querySelectorAll('*[data-hotline="true"]') + ) { + // Перебор элементов для инициализации бегущих строк + + if (typeof element.id === "string") { + // Найден идентификатор + + // Инициализация инстанции бегущей строки + const hotline = new this(element.id, element); + + for (const attribute of element.getAttributeNames()) { + // Перебор аттрибутов + + // Запись параметра в инстанцию бегущей строки + hotline.configure(attribute); } - offset(value) { - // Запись отступа - this.#shell.firstElementChild.style[ - this.vertical ? "marginTop" : "marginLeft" - ] = value + "px"; + // Запуск бегущей строки + hotline.start(); - if (this.events.get("offset")) { - // Запрошен вызов события: "сдвиг" + // Запись инстанции бегущей строки в элемент + element.hotline = hotline; - // Вызов события: "сдвиг" - document.dispatchEvent( - new CustomEvent(`hotline.${this.#id}.offset`, { - detail: { - to: value, - }, - }), - ); - } + // Запись в счётчик успешных инициализаций + success.add(hotline); + } else ++error; + } - return this; - } + if (event) { + // Запрошен вызов события: "предварительная подготовка" - static preprocessing(event = false) { - // Инициализация счётчиков инстанций горячей строки - const success = new Set(); - let error = 0; - - for ( - const element of document.querySelectorAll('*[data-hotline="true"]') - ) { - // Перебор элементов для инициализации бегущих строк - - if (typeof element.id === "string") { - // Найден идентификатор - - // Инициализация инстанции бегущей строки - const hotline = new this(element.id, element); - - for (const attribute of element.getAttributeNames()) { - // Перебор аттрибутов - - // Запись параметра в инстанцию бегущей строки - hotline.configure(attribute); - } - - // Запуск бегущей строки - hotline.start(); - - // Запись инстанции бегущей строки в элемент - element.hotline = hotline; - - // Запись в счётчик успешных инициализаций - success.add(hotline); - } else ++error; - } - - if (event) { - // Запрошен вызов события: "предварительная подготовка" - - // Вызов события: "предварительная подготовка" - document.dispatchEvent( - new CustomEvent(`hotline.preprocessed`, { - detail: { - success, - error, - }, - }), - ); - } - } - }; + // Вызов события: "предварительная подготовка" + document.dispatchEvent( + new CustomEvent(`hotline.preprocessed`, { + detail: { + success, + error, + }, + }), + ); } } -}); +} diff --git a/mirzaev/arming_bot/system/public/js/loader.js b/mirzaev/arming_bot/system/public/js/loader.js deleted file mode 100755 index bf019e7..0000000 --- a/mirzaev/arming_bot/system/public/js/loader.js +++ /dev/null @@ -1,265 +0,0 @@ -"use strict"; - -// Import dependencies -import("/js/core.js").then(() => - import("/js/damper.js").then(() => { - const dependencies = setInterval(() => { - if (typeof core === "function" && typeof core.damper === "function") { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); - - function initialization() { - if (typeof core.loader === "undefined") { - // Not initialized - - /** - * @name Loader - * - * @description - * Implements actions with loading and rendering content - * - * @memberof core - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich - */ - core.loader = class loader { - /** - * Load - * - * @param {string} uri - * @param {string} body - * - * @return {Promise} - */ - static async load(uri = "/", body) { - if (typeof uri === "string") { - // Received and validated uri - - return await core - .request( - uri, - body, - "POST", - { - "Content-Type": "application/x-www-form-urlencoded", - Accept: "application/json", - }, - "json" - ) - .then((json) => { - if ( - json.errors !== null && - typeof json.errors === "object" && - json.errors.length > 0 - ) { - // Fail (received errors) - } else { - // Success (not received errors) - - // Writing to the browser history - history.pushState({}, json.title ?? uri, uri); - - /** - * The - * - * The title of the page - */ - if ( - typeof json.title === "string" && - json.title.length > 0 - ) { - // Received text for the <title> of the page - - // Search for the <title> element (document.title) - const title = document.getElementsByTagName("title")[0]; - - 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("title"); - - // Inititalize the <head> element (document.head) - const head = document.getElementsByTagName("head")[0]; - - if (head instanceof HTMLElement) { - // Found the <head> element - - // Writing the <title> element into the <head> element - head.appendChild(title); - } - - // Writing title into the <title> element - title.innerText = json.title; - } - } - - /** - * The <header> element - */ - if ( - typeof json.header === "string" && - json.header.length > 0 - ) { - // Received and validated the header HTML-code - - if (core.header instanceof HTMLElement) { - // Found the <header> element - - // Writing into the <header> element - core.header.outerHTML = json.header; - - // Reinitializing the parameter with the <header> element - core.header = - document.getElementsByTagName("header")[0]; - } else { - // Not found the <header> element - - // Initialize the <header> element - core.header = document.createElement("header"); - - // Inititalize the <body> element (document.body) - const body = document.getElementsByTagName("body")[0]; - - if (body instanceof HTMLElement) { - // Found the <body> element - - if (core.main instanceof HTMLElement) { - // Found the <main> element - - // Writing the <header> element before the <main> element - body.insertBefore(core.header, core.main); - } else if (core.footer instanceof HTMLElement) { - // Fount the <footer> element - - // Writing the <header> element before the <footer> element - body.insertBefore(core.header, core.footer); - } else { - // Not found the <main> element and the <footer> element - - // Search for the last <section> element inside the <body> element - const section = document.body.querySelector( - "body > section:last-of-type" - ); - - if (section instanceof HTMLElement) { - // Found the last <section> element inside the <body> element - - // Writing the <header> element after the last <section> element inside the <body> element - body.insertBefore( - core.header, - section.nextSibling - ); - } else { - // Not found section elements <section> inside the <body> element - - // Writing the <header> element into the <body> element - body.appendChild(core.header); - } - } - - // Writing into the <header> element - core.header.outerHTML = json.header; - - // Reinitializing the parameter with the <header> element - core.header = - document.getElementsByTagName("header")[0]; - } - } - } - - /** - * The <main> element - * - * The main content of the page - */ - if (typeof json.main === "string" && json.main.length > 0) { - // Received and validated the <main> HTML-code - - if (core.main instanceof HTMLElement) { - // Found the <main> element - - // Writing into the <main> element - core.main.outerHTML = json.main; - - // Reinitializing the parameter with the <main> element - core.main = document.getElementsByTagName("main")[0]; - } else { - // Not found the <main> element - - // Initialize the <main> element - core.main = document.createElement("main"); - - // Inititalize the <body> element (document.body) - const body = document.getElementsByTagName("body")[0]; - - if (body instanceof HTMLElement) { - // Found the <body> element - - if (core.header instanceof HTMLElement) { - // Found the <header> element - - // Writing the <main> element after the <header> element - body.insertBefore( - core.main, - core.header.nextSibling - ); - } else if (core.footer instanceof HTMLElement) { - // Fount the <footer> element - - // Writing the <main> element before the <footer> element - body.insertBefore(core.main, core.footer); - } else { - // Not found the <header> element and the <footer> element - - // Search for the last <section> element inside the <body> element - const section = document.body.querySelector( - "body > section:last-of-type" - ); - - if (section instanceof HTMLElement) { - // 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.nextSibling); - } else { - // Not found section elements <section> inside the <body> element - - // Writing the <main> element into the <body> element - body.appendChild(core.main); - } - } - - // Writing into the <main> element - core.main.outerHTML = json.main; - - // Reinitializing the parameter with the <main> element - core.main = document.getElementsByTagName("main")[0]; - } - } - } - - // Exit (success) - return json; - } - }); - } - } - }; - } - } - }) -); diff --git a/mirzaev/arming_bot/system/public/js/modules/account.js b/mirzaev/arming_bot/system/public/js/modules/account.js new file mode 100755 index 0000000..dec5d0a --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/account.js @@ -0,0 +1,202 @@ +"use strict"; + +/** + * @name Account + * + * @description + * Implements actions with accounts + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default class account { + /** + * @name Type of the program + */ + static type = "module"; + + // Wrap of indicator of the account + static wrap = document.getElementById("account"); + + // Indicator of the account + static indicator = this.wrap?.getElementsByTagName("i")[0] ?? null; + + // Description of the account + static description = this.wrap?.getElementsByTagName("small")[0] ?? null; + + /** + * Authentication + * + * @return {void} + */ + static authentication() { + core.status_loading.removeAttribute("disabled"); + + const timer_for_response = setTimeout(() => { + core.status_loading.setAttribute("disabled", true); + }, 3000); + + core.modules.connect("telegram").then(() => { + // Imported the telegram module + + if (core.telegram.api.initData.length > 0) { + core + .request( + "/session/connect/telegram", + core.telegram.api.initData, + ) + .then((json) => { + if (json) { + // Received a JSON-response + + if ( + json.errors !== null && + typeof json.errors === "object" && + json.errors.length > 0 + ) { + // Errors received + } else { + // Errors not received + + if (json.connected === true) { + core.status_loading.setAttribute("disabled", true); + clearTimeout(timer_for_response); + + const a = core.status_account.getElementsByTagName("a")[0]; + a.setAttribute("onclick", "core.account.profile()"); + a.innerText = json.domain.length > 0 + ? "@" + json.domain + : "ERROR"; + } + + if ( + json.language !== null && + typeof json.language === "string" && + json.langiage.length === 2 + ) { + core.language = json.language; + } + } + } + }); + } + }); + } + + /** + * @name Buffer + */ + static buffer = class buffer { + /** + * @name Write + * + * @description + * Write to the account buffer (interface) + * + * @param {string} name Name of the parameter + * @param {string} value Value of the parameter (it can be JSON) + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Execution completed with an error? + */ + static write = (name, value, force = false) => { + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.write.damper(name, value, force); + }, + () => { + // Not imported the damper module + + // Execute + this.write.system(name, value, force); + }, + ); + + // Exit (success) + return false; + }; + }; +} + +core.modules.connect("damper").then(() => { + // Imported the damper module + + Object.assign( + account.buffer.write, + { + /** + * @name Write + * + * @description + * Write to the account buffer (damper) + * + * @param {string} name Name of the parameter + * @param {string} value Value of the parameter (it can be JSON) + * @param {bool} force Ignore the damper? (false) + * + * @return {Promise} + */ + damper: core.damper( + (...variables) => account.buffer.write.system(...variables), + 300, + 3, + ), + }, + ); +}); + +Object.assign( + account.buffer.write, + { + /** + * @name Write + * + * @description + * Write to the account buffer (system) + * + * @param {string} name Name of the parameter + * @param {string} value Value of the parameter (it can be JSON) + * + * @return {Promise} + */ + async system( + name, + value, + resolve = () => {}, + reject = () => {}, + ) { + try { + if (typeof name === "string" && typeof value === "string") { + // Received and validated required arguments + + // Sending request to the server + return await core.request( + "/account/write", + `${name}=${value}`, + "POST", + ) + .then( + (json) => { + if (json) { + // Received a JSON-response + + // Exit (success) + resolve(json); + } + }, + () => reject(), + ); + } + } catch (e) { + // Exit (fail) + reject(e); + } + }, + }, +); + +// Connecting to the core +if (!core.account) core.account = account; diff --git a/mirzaev/arming_bot/system/public/js/modules/cart.js b/mirzaev/arming_bot/system/public/js/modules/cart.js new file mode 100755 index 0000000..30e1d05 --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/cart.js @@ -0,0 +1,528 @@ +"use strict"; + +/** + * @name Cart + * + * @description + * Implements actions with cart + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default class cart { + /** + * @name Type of the program + */ + static type = "module"; + + /** + * Toggle + * + * Toggle the product in the cart (interface) + * + * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product + * @param {HTMLElement} product The product element + * @param {bool} remove Remove the product element if json.amount === 0? + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Execution completed with an error? + */ + static toggle(element, product, remove = false, force = false) { + // Blocking the element + element.setAttribute("disabled", "true"); + + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.product.damper( + element, + product, + "toggle", + undefined, + remove, + force, + ); + }, + () => { + // Not imported the damper module + + // Execute + this.product.system( + element, + product, + "toggle", + undefined, + remove, + force, + ); + }, + ); + + // Exit (success) + return false; + } + + /** + * Write + * + * Write the product in the cart (interface) + * + * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product + * @param {HTMLElement} product The product element + * @param {number} amount Amount of writings + * @param {bool} remove Remove the product element if json.amount === 0? + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Execution completed with an error? + */ + static write( + element, + product, + amount = 1, + remove = false, + force = false, + ) { + // Blocking the element + element.setAttribute("disabled", "true"); + + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.product.damper( + element, + product, + "write", + amount, + remove, + force, + ); + }, + () => { + // Not imported the damper module + + // Execute + this.product.system( + element, + product, + "write", + amount, + remove, + force, + ); + }, + ); + + // Exit (success) + return false; + } + + /** + * Delete + * + * Delete the product from the cart (interface) + * + * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product + * @param {HTMLElement} product The product element + * @param {number} amount Amount of deletings + * @param {bool} remove Remove the product element if json.amount === 0? + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Execution completed with an error? + */ + static delete( + element, + product, + amount = 1, + remove = false, + force = false, + ) { + // Blocking the element + element.setAttribute("disabled", "true"); + + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.product.damper( + element, + product, + "delete", + amount, + remove, + force, + ); + }, + () => { + // Not imported the damper module + + // Execute + this.product.system( + element, + product, + "delete", + amount, + remove, + force, + ); + }, + ); + + // Exit (success) + return false; + } + + /** + * Set + * + * Set amount of the product in the cart (interface) + * + * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product + * @param {HTMLElement} product The product element + * @param {number} amount Amount of the product in the cart to be setted + * @param {bool} remove Remove the product element if json.amount === 0? + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Execution completed with an error? + */ + static set( + element, + product, + amount = 1, + remove = false, + force = false, + ) { + // Blocking the element + element.setAttribute("disabled", "true"); + + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.product.damper( + element, + product, + "set", + amount, + remove, + force, + ); + }, + () => { + // Not imported the damper module + + // Execute + this.product.system( + element, + product, + "set", + amount, + remove, + force, + ); + }, + ); + + // Exit (success) + return false; + } + + /** + * The product + * + * Handle the product in the cart (system) + * + * @param {HTMLButtonElement|HTMLInputElement} element Handler element of the product + * @param {HTMLElement} product The product element + * @param {string} type Type of action with the product + * @param {number} amount Amount of product to handle + * @param {bool} remove Remove the product element if json.amount === 0? + * + * @return {bool} Execution completed with an error? + */ + static product( + element, + product, + type, + amount = null, + remove = false, + ) { + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.product.damper( + element, + product, + type, + amount, + remove, + force, + ); + }, + () => { + // Not imported the damper module + + // Execute + this.product.system( + element, + product, + type, + amount, + remove, + force, + ); + }, + ); + + // Exit (success) + return false; + } + + /** + * @name Summary + * + * @description + * Initialize summary of products the cart (system) + * + * @return {Promise} + */ + static async summary() { + // Request + return await core.request("/cart/summary") + .then((json) => { + if (json) { + // Received a JSON-response + + if ( + json.errors !== null && + typeof json.errors === "object" && + json.errors.length > 0 + ) { + // Fail (received errors) + } else { + // Success (not received errors) + + // Initializing the summary amount <span> element + const amount = document.getElementById("amount"); + + if (amount instanceof HTMLElement) { + // Initialized the summary amount element + + // Writing summmary amount into the summary amount element + amount.innerText = json.amount; + } + + // Initializing the summary cost <span> element + const cost = document.getElementById("cost"); + + if (cost instanceof HTMLElement) { + // Initialized the summary cost element + + // Writing summmary cost into the summary cost element + cost.innerText = json.cost; + } + } + } + }); + } +} + +core.modules.connect("damper").then(() => { + // Imported the damper module + + Object.assign( + cart.product, + { + /** + * Toggle + * + * Toggle the product in the cart (damper) + * + * @param {HTMLButtonElement|HTMLInputElement} element Handler elememnt of the product + * @param {HTMLElement} product The product element + * @param {bool} remove Remove the product element if json.amount === 0? + * @param {bool} force Ignore the damper? (false) + * + * @return {Promise} + */ + damper: core.damper( + (...variables) => cart.product.system(...variables).then(cart.summary), + 300, + 6, + ), + }, + ); +}); + +Object.assign( + cart.product, + { + /** + * The product + * + * Handle the product in the cart (system) + * + * @param {HTMLButtonElement|HTMLInputElement} element Handler element of the product + * @param {HTMLElement} product The product element + * @param {string} type Type of action with the product + * @param {number} amount Amount of product to handle + * @param {bool} remove Remove the product element if json.amount === 0? + * + * @return {Promise|null} + */ + async system( + element, + product, + type, + amount = null, + remove = false, + resolve = () => {}, + reject = () => {}, + ) { + try { + if ( + (element instanceof HTMLButtonElement || + element instanceof HTMLInputElement) && + product instanceof HTMLElement + ) { + // Validated + + // Initializing the buffer of request body + let request = ""; + + // Initializing of identifier of the product + const identifier = +product.getAttribute( + "data-product-identifier", + ); + + if (typeof identifier === "number") { + // Validated identifier + + // Writing to the buffer of request body + request += "&identifier=" + identifier; + + if ( + type === "toggle" || + type === "write" || + type === "delete" || + type === "set" + ) { + // Validated type + + // Writing to the buffer of request body + request += "&type=" + type; + + console.log(type, amount); + if ( + (type === "toggle" && + amount === null || + typeof amount === "undefined") || + (type === "set" && + amount === 0 || + amount === 100) || + typeof amount === "number" && + amount > 0 && + amount < 100 + ) { + // Validated amount + + console.log(amount); + if (type !== "toggle") { + // Not a toggle request + + // Writing to the buffer of request body + request += "&amount=" + amount; + } + + // Request + return await core.request( + "/cart/product", + request, + ) + .then( + (json) => { + if (json) { + // Received a JSON-response + + if ( + json.errors !== null && + typeof json.errors === "object" && + json.errors.length > 0 + ) { + // Fail (received errors) + } else { + // Success (not received errors) + + if (remove && json.amount === 0) { + // Requested deleting of the product element when there is no the product in the cart + + // Deleting the product element + product.remove(); + } else { + // Not requested deleting the product element when there is no the product in the cart + + // Unblocking the element + element.removeAttribute("disabled"); + + // Writing offset of hue-rotate to indicate that the product is in the cart + product.style.setProperty( + "--hue-rotate-offset", + json.amount + "0deg", + ); + + // Writing attribute with amount of the product in the cart + product.setAttribute( + "data-product-amount", + json.amount, + ); + + // Initializing the amount <span> element + const amounts = product.querySelectorAll( + '[data-product-parameter="amount"]', + ); + + for (const amount of amounts) { + // Iterating over an amount elements + + if (amount instanceof HTMLInputElement) { + // The <input> element + + // Writing amount of the product in the cart + amount.value = json.amount; + } else { + // Not the <input> element + + // Writing amount of the product in the cart + amount.innerText = json.amount; + } + } + } + + // Exit (success) + resolve(); + } + } + }, + () => reject(), + ); + } + } + } + } + } catch (e) { + // Exit (fail) + reject(e); + } + }, + }, +); + +// Connecting to the core +if (!core.cart) core.cart = cart; diff --git a/mirzaev/arming_bot/system/public/js/modules/catalog.js b/mirzaev/arming_bot/system/public/js/modules/catalog.js new file mode 100755 index 0000000..4bf0db2 --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/catalog.js @@ -0,0 +1,1236 @@ +"use strict"; + +/** + * @name Catalog + * + * @description + * Implements actions with catalog + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default class catalog { + /** + * @name Type of the program + */ + static type = "module"; + + /** + * Parameters of search + * + * Will be converted to the body of the GET request ("?name=value&name2=value2") + */ + static parameters = new Map([ + [ + "text", + new URLSearchParams(document.location.search).get("text"), + ], + [ + "category", + new URLSearchParams(document.location.search).get( + "category", + ), + ], + [ + "sort", + new URLSearchParams(document.location.search).get("sort"), + ], + [ + "brand", + new URLSearchParams(document.location.search).get("brand"), + ], + ]); + + /** + * Search (interface) + * + * Request search in the catalog and render in the core.main + * + * @param {Event} event Event (keyup) + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} True if an error occurs to continue the event execution + */ + static search(event, force = false) { + // Initializing the search <input> element + const input = document.querySelector("search#search>input"); + + if (input instanceof HTMLElement) { + // Found the search <input> element + + // Removing errors animations + input.classList.remove("error"); + + if ( + input.value.length < 3 && + input.value.lenght > 0 + ) { + // Only 1-2 character entered (this is not a search reset and is not an adequate search) + + // Exit (fail) + return true; + } + } + + if (event instanceof Event) { + // Executed from event + + if (event.type === "keyup") { + // Executed by "keyup" event + + if (event.target === input) { + // Executed from search <input> element + + if (event.keyCode === 13) { + // Executed by "enter" button + + // Blocking the search <input> element + input.setAttribute( + "disabled", + true, + ); + + // Execute + this.search.system(); + } else if (input.value.length < 3) { + // Less than 3 character entered (this is not a search reset and is not an adequate search) + + // Exit (fail) + return true; + } else { + // Executed by any else button + + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.search.damper(force); + }, + () => { + // Not imported the damper module + + // Execute under damper + this.search.system(force); + }, + ); + } + } + } else { + // Executed not by "keyup" event + + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.search.damper(force); + }, + () => { + // Not imported the damper module + + // Execute under damper + this.search.system(force); + }, + ); + } + + // Exit (success) + return false; + } + + // Exit (fail) + return true; + } + + /** + * Product card (interface) + * + * Request product data and render in core.window + * + * @param {HTMLElement} button Button of the product <a> + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} True if an error occurs to continue the event execution + */ + static product(button, force = false) { + // Initializing identifier of the category + const identifier = button.parentElement.getAttribute( + "data-product-identifier", + ); + + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.product.damper(identifier, force); + }, + () => { + // Not imported the damper module + + // Execute + this.product.system(identifier, force); + }, + ); + + // Exit (success) + return false; + } +} + +core.modules.connect("damper").then(() => { + // Imported the damper module + + Object.assign( + catalog.search, + { + /** + * Search (damper) + * + * Request search in the catalog and render in the core.main + * + * @param {bool} force Ignore the damper? (false) + * + * @return {void} + */ + damper: core.damper( + (...variables) => catalog.search.system(...variables), + 1400, + 1, + ), + }, + ); + + Object.assign( + catalog.product, + { + /** + * Product card (damper) + * + * Request product data and render in core.window + * + * @param {string} identifier Identifier of the product + * @param {bool} force Ignore the damper? (false) + * + * @return {void} + */ + damper: core.damper( + (...variables) => catalog.product.system(...variables), + 400, + 2, + ), + }, + ); +}); + +Object.assign( + catalog.product, + { + /** + * Product card (system) + * + * Request product data and render in core.window + * + * @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).then( + (json) => { + if (json) { + // Received a JSON-response + + if ( + json.errors !== null && + typeof json.errors === "object" && + json.errors.length > 0 + ) { + // Fail (received errors) + } else { + // Success (not received errors) + + if (core.window instanceof HTMLElement) { + // Initialized active window + + // Delete active window + core.window.remove(); + } + + core.modules.connect("telegram").then(() => { + // 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.classList.add("images", "unselectable"); + + const button = core.telegram.api.isVisible; + + // блокировка закрытия изображений + let images_from; + const _images_from = (event) => (images_from = event); + images.addEventListener("mousedown", _images_from); + images.addEventListener("touchstart", _images_from); + + const _open = (event) => { + if ( + event.type === "touchstart" || + event.button === 0 + ) { + { + let x = event.pageX || event.touches[0].pageX; + let y = event.pageY || event.touches[0].pageY; + let _x = images_from.pageX || + images_from.touches[0].pageX; + let _y = images_from.pageY || + images_from.touches[0].pageY; + + if ( + _x - x < 10 && + _x - x > -10 && + _y - y < 10 && + _y - y > -10 + ) { + images.classList.add("extend"); + + if (button) { + core.telegram.api.MainButton.hide(); + } + + setTimeout(() => { + images.hotline.step = 0; + images.hotline.wheel = false; + images.hotline.start(); + images.addEventListener("mouseup", _close); + images.addEventListener("touchend", _close); + }, 300); + images.removeEventListener("mouseup", _open); + images.removeEventListener("touchend", _open); + } + } + } + }; + + const _close = (event) => { + let x = event.pageX || event.touches[0].pageX; + let y = event.pageY || event.touches[0].pageY; + let _x = images_from.pageX || + images_from.touches[0].pageX; + let _y = images_from.pageY || + images_from.touches[0].pageY; + + if ( + event.type === "touchstart" || + event.button === 0 + ) { + if ( + _x - x < 10 && + _x - x > -10 && + _y - y < 10 && + _y - y > -10 + ) { + // Курсор не был сдвинут на 10 квардратных пикселей + // (в идеале надо переделать на таймер 2 секунды есди зажата кнопка то ничего не делать а просто двигать) + // пох абсолютно сейчас заказчик слишком охуевший для этого + + images.hotline.step = -0.3; + images.hotline.wheel = true; + + if (width < card.offsetWidth) { + images.hotline.stop(); + } + images.classList.remove("extend"); + + if (button) { + core.telegram.api.MainButton.show(); + } + + images.removeEventListener("mouseup", _close); + images.removeEventListener("touchend", _close); + images.addEventListener("mousedown", _start); + images.addEventListener("touchstart", _start); + } + } + }; + + 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); + + for (const uri of json.product.images) { + const image = document.createElement("img"); + image.setAttribute("src", uri); + image.setAttribute("ondragstart", "return false;"); + + images.append(image); + } + + 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.append(name); + exit.append(exit_icon); + h3.append(exit); + card.append(h3); + card.append(images); + header.append(brand); + card.append(header); + card.append(description); + card.append(compatibility); + footer.append(dimensions); + footer.append(weight); + footer.append(cost); + card.append(footer); + wrap.append(card); + document.body.append(wrap); + + // Reinitialize parameter + core.window = document.getElementById("window"); + + let width = 0; + let buffer; + [...images.children].forEach( + (child) => (width += child.offsetWidth + + (isNaN( + buffer = parseFloat( + getComputedStyle(child).marginRight, + ), + ) + ? 0 + : buffer)), + ); + + // блокировка закрытия карточки + 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(); + } + + 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); + }; + + 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); + + import("../hotline.js").then((hotline) => { + // Imported the hotline module + + images.hotline = new hotline.default( + json.product.identfier, + images, + ); + images.hotline.step = -0.3; + images.hotline.wheel = true; + images.hotline.touch = true; + + if (width > card.offsetWidth) { + images.hotline.start(); + } + }); + } + }); + + // Exit (success) + resolve(); + } + } + }, + () => reject(), + ); + } + } catch (e) { + // Exit (fail) + reject(e); + } + }, + }, +); + +Object.assign( + catalog.search, + { + /** + * Search (system) + * + * Request search in the catalog and render in the core.main + * + * @return {Promise} Request to the server + * + * @todo add animations of errors + */ + async system( + resolve = () => {}, + reject = () => {}, + ) { + try { + // @todo add timeout to unblock + + // Initialize the buffer of URN parameters + const parameters = new URLSearchParams( + window.location.search, + ); + + // Iterate parameters + catalog.parameters.forEach((value, parameter) => { + if ( + (typeof value === "string" || + typeof value === "number") && + !( + (parameter === "text" && value === null) || + value.length < 3 + ) + ) { + // Validated value + + // Write parameter to the buffer of URN parameters + parameters.set(parameter, value); + } else parameters.delete(parameter); + }); + + return await core.modules.connect("loader").then(async () => { + // Imported the loader module + + return await core.loader.load("?" + parameters) + .then( + (json) => { + if (json) { + // Received a JSON-response + + // Initializing the search <input> element + const input = document.querySelector("search#search>input"); + + if (input instanceof HTMLElement) { + // Found the search <input> element + + input.removeAttribute("disabled"); + input.focus(); + } + + /** + * Title <h2> + */ + if ( + typeof json.title === "string" && + json.title.length > 0 + ) { + // Received and validated text of the title <h2> + + // Initializing the title <h2> element + const title = document.getElementById("title"); + + if (title instanceof HTMLElement) { + // Found the title <h2> element + + // Writing into the title <h2> element + title.innerText = json.title; + } else { + // Not found the title <h2> element + + // Initialize the title <h2> element + const title = document.createElement("h2"); + + if (core.main instanceof HTMLElement) { + // Found the <main> element + + // Inititalize the first element in the <main> element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the <main> element + + // Writing the title <h2> element before the first element in the <main> element + core.main.insertBefore( + title, + first, + ); + } + } + + // Writing into the title <h2> element + title.innerText = json.title; + } + } else { + // Not received or not validated text of the title <h2> + + // Deleting the title <h2> element + document.getElementById("title")?.remove(); + } + + /** + * Search search <search> + */ + if ( + typeof json.search === "string" && + json.search.length > 0 + ) { + // Received and validated the searcn HTML-code + + // Initializing the search <search> element + const search = document.getElementById("search"); + + if (search instanceof HTMLElement) { + // Found the search <search> element + + // Writing into the search <search> element + search.outerHTML = json.search; + } else { + // Not found the search <search> element + + // Initialize the search <search> element + const search = document.createElement("search"); + + if (core.main instanceof HTMLElement) { + // Found the <main> element + + // Initializing the title <h2> element + const title = document.getElementById("title"); + + if (title instanceof HTMLElement) { + // Initialized the title <h2> elemment in the <main> element + + // Writing the search <search> element after the title <h2> element in the <main> element + core.main.insertBefore( + search, + title.nextSibling, + ); + } else { + // Not initialized the title <h2> element in the <main> element + + // Inititalize the first element in the <main> element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the <main> element + + // Writing the search <search> element before the first element in the <main> element + core.main.insertBefore( + search, + first, + ); + } + } + } + + // Writing into the search <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 <search> element + + // Deleting the search <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 <section> element + const categories = document.getElementById("categories"); + + if (categories instanceof HTMLElement) { + // Found the categories <section> element + + // Writing into the categories <section> element + categories.outerHTML = json.categories; + } else { + // Not found the categories <section> element + + // Initialize the categories <section> element + const categories = document.createElement("section"); + + if (core.main instanceof HTMLElement) { + // Found the <main> element + + // Initializing the search <search> element + const search = document.getElementById("search"); + + if (search instanceof HTMLElement) { + // Initialized the search <search> element in the <main> element + + // Writing the categories <section> element after the search <search> element in the <main> element + core.main.insertBefore( + categories, + search.nextSibling, + ); + } else { + // Not initialized the search <search> element in the <main> element + + // Initializing the title <h2> element + const title = document.getElementById("title"); + + if ( + title instanceof HTMLElement + ) { + // Initialized the title <h2> element in the <main> element + + // Writing the categories <section> element after the title <h2> element in the <main> element + core.main.insertBefore( + categories, + title.nextSibling, + ); + } else { + // Not initialized the title <h2> element in the <main> element + + // Inititalize the first element in the <main> element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the <main> element + + // Writing the categories <section> before the first element in the <main> element + core.main.insertBefore( + categories, + first, + ); + } + } + } + } + + // Writing into the categories <section> element + categories.outerHTML = json.categories; + } + } else { + // Not received or not validated the categories HTML-code + + // Deleting the categories <section> 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 <section> element + const filters = document.getElementById("filters"); + + if (filters instanceof HTMLElement) { + // Found the filters <section> element + + // Writing into the filters <section> element + filters.outerHTML = json.filters; + } else { + // Not found the filters <section> element + + // Initialize the filters <section> element + const filters = document.createElement("section"); + + if (core.main instanceof HTMLElement) { + // Found the <main> element + + // Initializing the categories <section> element + const categories = document.getElementById( + "categories", + ); + + if ( + categories instanceof HTMLElement + ) { + // Initialized the categories <section> element in the <main> element + + // Writing the filters <section> element after the categories <section> element in the <main> element + core.main.insertBefore( + filters, + categories.nextSibling, + ); + } else { + // Not initialized the categories <section> element in the <main> element + + // Initializing the search <search> element + const search = document.getElementById("search"); + + if ( + search instanceof HTMLElement + ) { + // Initialized the search <search> element in the <main> element + + // Writing the filters <section> element after the search <search> element in the <main> element + core.main.insertBefore( + filters, + search.nextSibling, + ); + } else { + // Not initialized the search <search> element in the <main> element + + // Initializing the title <h2> element + const title = document.getElementById("title"); + + if ( + title instanceof HTMLElement + ) { + // Initialized the title <h2> element in the <main> element + + // Writing the filters <section> element after the title <h2> element in the <main> element + core.main.insertBefore( + filters, + title.nextSibling, + ); + } else { + // Not initialized the title <h2> element in the <main> element + + // Inititalize the first element in the <main> element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the <main> element + + // Writing the filters <section> before the first element in the <main> element + core.main.insertBefore( + filters, + first, + ); + } + } + } + } + } + + // Writing into the filters <section> element + filters.outerHTML = json.filters; + } + } else { + // Not received or not validated the filters HTML-code + + // Deleting the filters <section> 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 <section> element + const sorting = document.getElementById("sorting"); + + if (sorting instanceof HTMLElement) { + // Found the sorting <section> element + + // Writing into the sorting <section> element + sorting.outerHTML = json.sorting; + } else { + // Not found the sorting <section> element + + // Initialize the sorting <section> element + sorting = document.createElement("section"); + + if (core.main instanceof HTMLElement) { + // Found the <main> element + + // Initializing the filters <section> element + const filters = document.getElementById("filters"); + + if (filters instanceof HTMLElement) { + // Initialized the filters <section> element in the <main> element + + // Writing the sorting <section> element after the filters <section> element in the <main> element + core.main.insertBefore( + sorting, + filters.nextSibling, + ); + } else { + // Not initialized the filters <section> element in the <main> element + + // Initializing the categories <section> element + const categories = document.getElementById( + "categories", + ); + + if ( + categories instanceof HTMLElement + ) { + // Initialized the categories <section> element in the <main> element + + // Writing the sorting <section> element after the categories <section> element in the <main> element + core.main.insertBefore( + sorting, + categories.nextSibling, + ); + } else { + // Notnitialized the categories <section> element in the <main> element + + // Initializing the search <search> element + const search = document.getElementById("search"); + + if ( + search instanceof HTMLElement + ) { + // Initialized the search <search> element in the <main> element + + // Writing the sorting <section> element after the search <search> element in the <main> element + core.main.insertBefore( + sorting, + search.nextSibling, + ); + } else { + // Not nitialized the search <search> element in the <main> element + + // Initializing the title <h2> element + const title = document.getElementById("title"); + + if ( + title instanceof HTMLElement + ) { + // Initialized the title <h2> element in the <main> element + + // Writing the sorting <section> element after the title <h2> element in the <main> element + core.main.insertBefore( + sorting, + title.nextSibling, + ); + } else { + // Not initialized the title <h2> element in the <main> element + + // Inititalize the first element in the <main> element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the <main> element + + // Writing the sorting <section> before the first element in the <main> element + core.main.insertBefore( + sorting, + first, + ); + } + } + } + } + } + } + + // Writing into the sorting <section> element + sorting.outerHTML = json.sorting; + } + } else { + // Not received or not validated the sorting HTML-code + + // Deleting the sorting <section> 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 <section> element + const products = document.getElementById("products"); + + if (products instanceof HTMLElement) { + // Found the products <section> element + + // Writing into the products <section> element + products.outerHTML = json.products; + } else { + // Not found the products <section> element element + + // Initialize the products <section> element + const products = document.createElement("section"); + + if (core.main instanceof HTMLElement) { + // Found the <main> element + + // Initializing the sorting <section> element + const sorting = document.getElementById("sorting"); + + if (sorting instanceof HTMLElement) { + // Initialized the sorting <section> element in the <main> element + + // Writing the products <section> element after the sorting <section> element in the <main> element + core.main.insertBefore( + products, + sorting.nextSibling, + ); + } else { + // Initialized the sorting <section> element in the <main> element + + // Initializing the filters <section> element + const filters = document.getElementById("filters"); + + if ( + filters instanceof HTMLElement + ) { + // Initialized the filters <section> element in the <main> element + + // Writing the products <section> element after the filters <section> element in the <main> element + core.main.insertBefore( + products, + filters.nextSibling, + ); + } else { + // Not initialized the filters <section> element in the <main> element + + // Initializing the categories <section> element + const categories = document.getElementById( + "categories", + ); + + if ( + categories instanceof HTMLElement + ) { + // Initialized the categories <section> element in the <main> element + + // Writing the products <section> element after the categories <section> element in the <main> element + core.main.insertBefore( + products, + categories.nextSibling, + ); + } else { + // Not initialized the categories <section> element in the <main> element + + // Initializing the search <search> element + const search = document.getElementById("search"); + + if ( + search instanceof HTMLElement + ) { + // Initialized the search <search> element in the <main> element + + // Writing the products <section> element after the search <search> element in the <main> element + core.main.insertBefore( + products, + search.nextSibling, + ); + } else { + // Not initialized the search <search> element in the <main> element + + // Initializing the title <h2> element + const title = document.getElementById("title"); + + if ( + title instanceof HTMLElement + ) { + // Initialized the title <h2> element in the <main> element + + // Writing the products <section> element after the title <h2> element in the <main> element + core.main.insertBefore( + products, + title.nextSibling, + ); + } else { + // Not initialized the title <h2> element in the <main> element + + // Inititalize the first element in the <main> element + const first = core.main.firstElementChild; + + if (first instanceof HTMLElement) { + // Initialized the first element in the <main> element + + // Writing the products <section> before the first element in the <main> element + core.main.insertBefore( + products, + first, + ); + } + } + } + } + } + } + } + + // Writing into the products <section> element + products.outerHTML = json.products; + } + } else { + // Not received or not validated the products HTML-code + + // Deleting the products <section> element + document.getElementById("products")?.remove(); + } + } + + // Exit (success) + resolve(); + }, + () => reject(), + ); + }); + } catch (e) { + // Exit (fail) + reject(e); + } + }, + }, +); + +// Connecting to the core +if (!core.catalog) core.catalog = catalog; diff --git a/mirzaev/arming_bot/system/public/js/modules/connection.js b/mirzaev/arming_bot/system/public/js/modules/connection.js new file mode 100755 index 0000000..d95cea8 --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/connection.js @@ -0,0 +1,305 @@ +"use strict"; + +/** + * @name Connection + * + * @description + * Implements actions with websocket connection + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default class connection { + /** + * @name Type of the program + */ + static type = "module"; + + // Wrap of indicator of the connection + static wrap = document.getElementById("connection"); + + // Indicator of the connection + static indicator = this.wrap?.getElementsByTagName("i")[0]; + + // Description of the connection + static description = this.wrap?.getElementsByTagName("small")[0]; + + // Statuc of the connection + static connected = false; + + // Duration of the disconnected status + static timeout = 0; + + // Instance of the time counter in disconnected status + static counter; + + // Socket address (xn--e1ajlli это сокет) + static socket = "wss://arming.dev.mirzaev.sexy:9502"; + + // Instance of connection to the socket + static session; + + // Iterval for reconnect + static interval; + + // Attempts to connect (when connection.readyState === 0) + static attempts = 0; + + // Interval for block + static block; + + /** + * Initialize status of the connection to socket + * + * @param {bool} connected Connected? + * + * @return {void} + */ + static status(connected = false) { + if (this.indicator instanceof HTMLElement) { + // Initialized the indicator + + this.connected = connected; + + if (this.connected) { + // Connected + + this.wrap.setAttribute("title", "Connected"); + + this.indicator.classList.remove("disconnected"); + this.indicator.classList.add("connected"); + + clearInterval(this.counter); + this.description.innerText = ""; + this.counter = undefined; + this.timeout = 0; + } else { + // Disconnected + + this.wrap.setAttribute("title", "Disconnected"); + + this.indicator.classList.remove("connected"); + this.indicator.classList.add("disconnected"); + + if (typeof this.counter === "undefined") { + this.counter = setInterval(() => { + this.timeout += 0.01; + this.description.innerText = this.timeout.toFixed(2); + }, 10); + } + } + } + } + + /** + * Connect to the socket + * + * @param {bool|number} interval Connection check interval (ms) + * @param {function} preprocessing Will be executed every cycle + * @param {function} onmessage New message + * @param {function} onopen Connection opened + * @param {function} onclose Connection closed + * @param {function} onerror An error has occurred + * + * @return {Promise} + */ + static connect( + interval = false, + preprocessing, + onmessage, + onopen, + onclose, + onerror, + ) { + return new Promise((resolve, reject) => { + try { + if (typeof interval === "number" && interval > 0) { + // Connect with automatic reconnect + + if (typeof this.interval === "undefined") { + this.interval = setInterval(() => { + preprocessing(); + + if ( + !(this.session instanceof WebSocket) || + (this.session.readyState === 3 || + this.session.readyState === 4) || + (this.session.readyState === 0 && + ++this.attempts > 10) + ) { + this.attempts = 0; + + if (this.session instanceof WebSocket) { + this.session.close(); + } + + this.session = new WebSocket(this.socket); + this.session.addEventListener("message", (e) => { + try { + const json = JSON.parse(e.data); + + if (json.type === "registration") { + // Подключение сокета к сессии + + fetch("/socket/registration", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: `key=${json.key}`, + }); + } + } catch (_e) {} + }); + this.session.addEventListener("message", onmessage); + this.session.addEventListener("open", onopen); + this.session.addEventListener("close", onclose); + this.session.addEventListener("error", onerror); + + resolve(this.session); + } else resolve(this.session); + }, interval); + } + } else { + // Connect without reconnecting + + if ( + !(this.session instanceof WebSocket) || + (this.session.readyState === 3 || + this.session.readyState === 4) + ) { + if (this.session instanceof WebSocket) { + this.session.close(); + } + + this.session = new WebSocket(this.socket); + this.session.addEventListener("message", onmessage); + this.session.addEventListener("open", onopen); + this.session.addEventListener("close", onclose); + this.session.addEventListener("error", onerror); + + resolve(this.session); + } else resolve(this.session); + } + } catch (_e) {} + }); + } + + /** + * Core is connected to the socket? + * + * @return {bool} + */ + static connected() { + return this.session instanceof WebSocket && + this.session.readyState === 1; + } +} + +connection.connect( + 3000, + () => + connection.status( + connection.session instanceof WebSocket && + connection.session.readyState === 1, + ), + (e) => { + try { + const json = JSON.parse(e.data); + + if (json.target === "task") { + // Заявка + + // Инициализация строки + const row = document.getElementById(json._key); + + if (row instanceof HTMLElement) { + // Инициализирована строка + + if (json.type === "blocked") { + // Заблокирована заявка + + // Запись статуса: "заблокирована" + row.setAttribute("data-blocked", json.account._key); + row.setAttribute( + "title", + "Редактирует: " + json.account.name, + ); + + // Удалить блокировку (60000 === 1 минута) (в базе данных стоит expires 1 минута тоже) + setTimeout(() => { + // Удаление статуса: "заблокирована" + row.removeAttribute("data-blocked"); + row.removeAttribute("title"); + + // Обновление строки + tasks.row(row); + }, 60000); + } else if (json.type === "unblocked") { + // Разблокирована заявка + + // Удаление статуса: "заблокирована" + row.removeAttribute("data-blocked"); + row.removeAttribute("title"); + + // Обновление строки + tasks.row(row); + } else if (json.type === "updated") { + // Обновлена заявка + + // Обновление строки + tasks.row(row); + } else if (json.type === "deleted") { + // Удалена заявка + + // Удаление строки + row.remove(); + } + } + } + } catch (_e) {} + // Инициализация идентифиатора + //const id = row.getAttribute("id"); + + // Инициализация количества непрочитанных сообщений + //const messages = row.lastElementChild.innerText; + + // Инициализация статуса активной строки + //const selected = row.getAttribute("data-selected"); + + // Реинициализация строки + //row.outerHTML = data.rows; + + // Реинициализация перезаписанной строки + //row = document.getElementById(id); + + // Копирование статуса активной строки + //if ( + // typeof selected === "string" && + // selected === "true" && + // document.body.contains(document.getElementById("popup")) + //) { + // row.setAttribute("data-selected", "true"); + //} + }, + (e) => { + //connection.status( + // connection instanceof WebSocket && + // connection.readyState === 1, + //) + //console.log("Connected to WebSocket!"); + }, + (e) => { + //connection.status( + // connection instanceof WebSocket && + // connection.readyState === 1, + //) + //console.log("Connection closed"); + }, + (e) => { + //console.log("Error happens"); + }, +); + +// Connecting to the core +if (!core.connection) core.connection = connection; diff --git a/mirzaev/arming_bot/system/public/js/modules/damper.js b/mirzaev/arming_bot/system/public/js/modules/damper.js new file mode 100755 index 0000000..aea8f4f --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/damper.js @@ -0,0 +1,76 @@ +"use strict"; + +/** + * @name Damper + * + * @description + * Execute multiple "function" calls in a "timeout" amount of time just once + * + * @param {function} function Function to execute after damping + * @param {number} timeout Timer in milliseconds (ms) + * @param {number} force Argument number storing the status of enforcement execution (see @example) + * + * @return {Promise} + * + * @example + * a = damper( + * async ( + * a, // 0 + * b, // 1 + * c, // 2 + * force = false, // 3 + * d, // 4 + * resolve, + * reject + * ) => { + * // Body of the function + * + * resolve(); + * }, + * 500, + * 3, // 3 -> "force" argument + * ); + * + * a('for a', 'for b', 'for c', true, 'for d'); // Force execute is enabled + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default function damper(func, timeout = 300, force) { + // Declaring of the timer for executing the function + let timer; + + return ((...args) => { + return new Promise((resolve, reject) => { + // Deinitializing of the timer + clearTimeout(timer); + + if (typeof force === "number" && args[force]) { + // Requested execution with ignoring the timer + + // Deleting the force argument + if (typeof force === "number") delete args[force - 1]; + + // Writing promise handlers into the arguments variable + args.push(resolve, reject); + + // Executing the function + func.apply(this, args); + } else { + // Normal execution + + // Deleting the force argument + if (typeof force === "number") delete args[force - 1]; + + // Writing promise handlers into the arguments variable + args.push(resolve, reject); + + // Resetting the timer and executing the function when the timer expires + timer = setTimeout(() => func.apply(this, args), timeout); + } + }); + }); +} + +// Connecting to the core +if (!core.damper) core.damper = damper; diff --git a/mirzaev/arming_bot/system/public/js/modules/delivery.js b/mirzaev/arming_bot/system/public/js/modules/delivery.js new file mode 100755 index 0000000..bd49a13 --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/delivery.js @@ -0,0 +1,161 @@ +"use strict"; + +/** + * @name Delivery + * + * @description + * Implements actions with delivery + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default class delivery { + /** + * @name Type of the program + */ + static type = "module"; + + /** + * @name Company + * + * @description + * Choose a delivery company (interface) + * + * @param {HTNLElement} element Handler element + * @param {string} identifier Identifier of the delivery company + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Execution completed with an error? + */ + static company = (element, identifier, force = false) => { + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.company.damper(element, identifier, force); + }, + () => { + // Not imported the damper module + + // Execute + this.company.system(element, identifier, force); + }, + ); + + // Exit (success) + return false; + }; + + /** + * @name City + * + * @description + * Write name of a city for delivery (interface) + * + * @param {HTNLInputElement} element Handler element <input> + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Execution completed with an error? + */ + static company = (element, force = false) => { + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.city.damper(element, identifier, force); + }, + () => { + // Not imported the damper module + + // Execute + this.company.system(element, identifier, force); + }, + ); + + // Exit (success) + return false; + }; +} + +core.modules.connect("damper").then(() => { + // Imported the damper module + + Object.assign( + delivery.company, + { + /** + * @name Write + * + * @description + * Choose a delivery company (damper) + * + * @param {HTNLElement} element Handler element + * @param {string} identifier Identifier of the delivery company + * @param {bool} force Ignore the damper? (false) + * + * @return {Promise} + */ + damper: core.damper( + (...variables) => delivery.company.system(...variables), + 300, + 3, + ), + }, + ); +}); + +Object.assign( + delivery.company, + { + /** + * @name Write + * + * @description + * Choose a delivery company (system) + * + * @param {string} identifier Identifier of the delivery company + * + * @return {Promise} + */ + async system( + type, + value, + resolve = () => {}, + reject = () => {}, + ) { + try { + if ( + typeof type === "string" && + (typeof value === "string" || typeof valye === "number") + ) { + // Received and validated required arguments + + // Sending request to the server + return await core.buffer.write(`delivery_${type}`, value) + .then( + (json) => { + try { + + + // Exit (success) + resolve(json); + } catch (e) { + // Exit (fail) + reject(e); + } + }, + () => reject(), + ); + } + } catch (e) { + // Exit (fail) + reject(e); + } + }, + }, +); + +// Connecting to the core +if (!core.delivery) core.delivery = delivery; diff --git a/mirzaev/arming_bot/system/public/js/modules/loader.js b/mirzaev/arming_bot/system/public/js/modules/loader.js new file mode 100755 index 0000000..14e9aa7 --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/loader.js @@ -0,0 +1,250 @@ +"use strict"; + +/** + * @name Loader + * + * @description + * Implements actions with loading and rendering content + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default class loader { + /** + * @name Type of the program + */ + static type = "module"; + + /** + * @name Load + * + * @param {string} uri + * @param {string} body + * + * @return {Promise} + */ + static async load(uri = "/", body) { + if (typeof uri === "string") { + // Received and validated uri + + return await core + .request( + uri, + body, + "POST", + { + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + }, + "json", + ) + .then((json) => { + if (json) { + // Received a JSON-response + + if ( + json.errors !== null && + typeof json.errors === "object" && + json.errors.length > 0 + ) { + // Fail (received errors) + } else { + // Success (not received errors) + + // Writing to the browser history + history.pushState({}, json.title ?? uri, uri); + + /** + * The <title> + * + * The title of the page + */ + if ( + typeof json.title === "string" && + json.title.length > 0 + ) { + // Received text for the <title> of the page + + // Search for the <title> element (document.title) + const title = document.getElementsByTagName("title")[0]; + + 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("title"); + + // Inititalize the <head> element (document.head) + const head = document.getElementsByTagName("head")[0]; + + if (head instanceof HTMLElement) { + // Found the <head> element + + // Writing the <title> element into the <head> element + head.appendChild(title); + } + + // Writing title into the <title> element + title.innerText = json.title; + } + } + + /** + * The <header> element + */ + if ( + typeof json.header === "string" && + json.header.length > 0 + ) { + // Received and validated the header HTML-code + + if (core.header instanceof HTMLElement) { + // Found the <header> element + + // Writing into the <header> element + core.header.outerHTML = json.header; + + // Reinitializing the parameter with the <header> element + core.header = document.getElementsByTagName("header")[0]; + } else { + // Not found the <header> element + + // Initialize the <header> element + core.header = document.createElement("header"); + + // Inititalize the <body> element (document.body) + const body = document.getElementsByTagName("body")[0]; + + if (body instanceof HTMLElement) { + // Found the <body> element + + if (core.main instanceof HTMLElement) { + // Found the <main> element + + // Writing the <header> element before the <main> element + body.insertBefore(core.header, core.main); + } else if (core.footer instanceof HTMLElement) { + // Fount the <footer> element + + // Writing the <header> element before the <footer> element + body.insertBefore(core.header, core.footer); + } else { + // Not found the <main> element and the <footer> element + + // Search for the last <section> element inside the <body> element + const section = document.body.querySelector( + "body > section:last-of-type", + ); + + if (section instanceof HTMLElement) { + // Found the last <section> element inside the <body> element + + // Writing the <header> element after the last <section> element inside the <body> element + body.insertBefore( + core.header, + section.nextSibling, + ); + } else { + // Not found section elements <section> inside the <body> element + + // Writing the <header> element into the <body> element + body.appendChild(core.header); + } + } + + // Writing into the <header> element + core.header.outerHTML = json.header; + + // Reinitializing the parameter with the <header> element + core.header = document.getElementsByTagName("header")[0]; + } + } + } + + /** + * The <main> element + * + * The main content of the page + */ + if (typeof json.main === "string" && json.main.length > 0) { + // Received and validated the <main> HTML-code + + if (core.main instanceof HTMLElement) { + // Found the <main> element + + // Writing into the <main> element + core.main.outerHTML = json.main; + + // Reinitializing the parameter with the <main> element + core.main = document.getElementsByTagName("main")[0]; + } else { + // Not found the <main> element + + // Initialize the <main> element + core.main = document.createElement("main"); + + // Inititalize the <body> element (document.body) + const body = document.getElementsByTagName("body")[0]; + + if (body instanceof HTMLElement) { + // Found the <body> element + + if (core.header instanceof HTMLElement) { + // Found the <header> element + + // Writing the <main> element after the <header> element + body.insertBefore( + core.main, + core.header.nextSibling, + ); + } else if (core.footer instanceof HTMLElement) { + // Fount the <footer> element + + // Writing the <main> element before the <footer> element + body.insertBefore(core.main, core.footer); + } else { + // Not found the <header> element and the <footer> element + + // Search for the last <section> element inside the <body> element + const section = document.body.querySelector( + "body > section:last-of-type", + ); + + if (section instanceof HTMLElement) { + // 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.nextSibling); + } else { + // Not found section elements <section> inside the <body> element + + // Writing the <main> element into the <body> element + body.appendChild(core.main); + } + } + + // Writing into the <main> element + core.main.outerHTML = json.main; + + // Reinitializing the parameter with the <main> element + core.main = document.getElementsByTagName("main")[0]; + } + } + } + + // Exit (success) + return json; + } + } + }); + } + } +} + +// Connecting to the core +if (!core.loader) core.loader = loader; diff --git a/mirzaev/arming_bot/system/public/js/modules/session.js b/mirzaev/arming_bot/system/public/js/modules/session.js new file mode 100755 index 0000000..5095e2b --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/session.js @@ -0,0 +1,129 @@ +"use strict"; + +/** + * @name Session + * + * @description + * Implements actions with sessions + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default class session { + /** + * @name Type of the program + */ + static type = "module"; + + /** + * @name Buffer + */ + static buffer = class buffer { + /** + * @name Write + * + * @description + * Write to the session buffer (interface) + * + * @param {string} name Name of the parameter + * @param {string} value Value of the parameter (it can be JSON) + * @param {bool} force Ignore the damper? (false) + * + * @return {bool} Execution completed with an error? + */ + static write = (name, value, force = false) => { + core.modules.connect("damper").then( + () => { + // Imported the damper module + + // Execute under damper + this.write.damper(name, value, force); + }, + () => { + // Not imported the damper module + + // Execute + this.write.system(name, value, force); + }, + ); + + // Exit (success) + return false; + }; + }; +} + +core.modules.connect("damper").then(() => { + // Imported the damper module + + Object.assign( + session.buffer.write, + { + /** + * @name Write + * + * @description + * Write to the session buffer (damper) + * + * @param {string} name Name of the parameter + * @param {string} value Value of the parameter (it can be JSON) + * @param {bool} force Ignore the damper? (false) + * + * @return {Promise} + */ + damper: core.damper( + (...variables) => session.buffer.write.system(...variables), + 300, + 3, + ), + }, + ); +}); + +Object.assign( + session.buffer.write, + { + /** + * @name Write + * + * @description + * Write to the session buffer (system) + * + * @param {string} name Name of the parameter + * @param {string} value Value of the parameter (it can be JSON) + * + * @return {Promise} + */ + async system( + name, + value, + resolve = () => {}, + reject = () => {}, + ) { + if (typeof name === "string" && typeof value === "string") { + // Received and validated required arguments + + // Sending request to the server + return await core.request( + "/session/write", + `${name}=${value}`, + "POST", + ) + .then( + (json) => { + if (json) { + // Received a JSON-response + + // Exit (success) + resolve(json); + } + }, + () => reject(), + ); + } + }, + }, +); + +// Connecting to the core +if (!core.session) core.session = session; diff --git a/mirzaev/arming_bot/system/public/js/modules/telegram.js b/mirzaev/arming_bot/system/public/js/modules/telegram.js new file mode 100755 index 0000000..bcf8ad3 --- /dev/null +++ b/mirzaev/arming_bot/system/public/js/modules/telegram.js @@ -0,0 +1,27 @@ +"use strict"; + +/** + * @name Telegram + * + * @description + * Implements actions with data of the telegram account + * + * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License + * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> + */ +export default class telegram { + /** + * @name Type of the program + */ + static type = "module"; + + /** + * Telegram "WebApp" API + * + * @see {@link https://core.telegram.org/bots/webapps#initializing-mini-apps} + */ + static api = window.Telegram.WebApp; +} + +// Connecting to the core +if (!core.telegram) core.telegram = telegram; diff --git a/mirzaev/arming_bot/system/public/js/session.js b/mirzaev/arming_bot/system/public/js/session.js deleted file mode 100755 index 00eec5a..0000000 --- a/mirzaev/arming_bot/system/public/js/session.js +++ /dev/null @@ -1,71 +0,0 @@ -"use strict"; - -// Import dependencies -import("/js/core.js").then(() => - import("/js/damper.js").then(() => - import("/js/telegram.js").then(() => { - const dependencies = setInterval(() => { - if ( - typeof core === "function" && - typeof core.damper === "function" && - typeof core.telegram === "function" - ) { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); - - function initialization() { - if (typeof core.session === "undefined") { - // Not initialized - - /** - * @name Session - * - * @description - * Implements actions with sessions - * - * @memberof core - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> - */ - core.session = class session { - /** - * Buffer - */ - static buffer = class buffer { - /** - * Write to the session buffer - * - * @param {string} name Name of the parameter - * @param {string} value Value of the parameter (it can be JSON) - * - * @return {void} - */ - static write(name, value) { - if (typeof name === "string" && typeof value === "string") { - // - - // Send request to the server - core.request( - "/session/write", - `${name}=${value}`, - "POST", - {}, - null, - ); - } - } - }; - }; - } - } - }) - ) -); diff --git a/mirzaev/arming_bot/system/public/js/telegram.js b/mirzaev/arming_bot/system/public/js/telegram.js deleted file mode 100755 index 67ac2a9..0000000 --- a/mirzaev/arming_bot/system/public/js/telegram.js +++ /dev/null @@ -1,53 +0,0 @@ -"use strict"; - -// Import dependencies -import("/js/core.js").then(() => - import("/js/damper.js").then(() => { - const dependencies = setInterval(() => { - if ( - typeof core === "function" && - typeof core.damper === "function" - ) { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); - - function initialization() { - if (typeof core.telegram === "undefined") { - // Not initialized - - /** - * @name Telegram - * - * @description - * Implements actions with data of the telegram account - * - * @memberof core - * - * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License - * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> - */ - core.telegram = class telegram { - /** - * Telegram "WebApp" API - * - * @see {@link https://core.telegram.org/bots/webapps#initializing-mini-apps} - */ - static api = window.Telegram.WebApp; - }; - - /* telegram.MainButton.text = - typeof core === "object" && core.language === "ru" - ? "Корзина" - : "Cart"; - telegram.MainButton.show(); */ - } - } - }) -); diff --git a/mirzaev/arming_bot/system/public/robot.php b/mirzaev/arming_bot/system/public/robot.php index 8bb905c..bd004ea 100755 --- a/mirzaev/arming_bot/system/public/robot.php +++ b/mirzaev/arming_bot/system/public/robot.php @@ -33,8 +33,12 @@ define('CATALOG_EXAMPLE', STORAGE . DIRECTORY_SEPARATOR . 'example.xlsx'); // Файл в формате xlsx для импорта каталога define('CATALOG_IMPORT', STORAGE . DIRECTORY_SEPARATOR . 'import.xlsx'); -// Ключ чат-робота Telegram -define('KEY', require(SETTINGS . DIRECTORY_SEPARATOR . 'key.php')); +/** + * Ключ чат-робота Telegram + * @deprecated + */ +define('KEY', require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php')); +define('TELEGRAM_KEY', require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php')); // Initialize dependencies require __DIR__ . DIRECTORY_SEPARATOR @@ -55,7 +59,7 @@ $config = new Config(); $config->setParseMode(Config::PARSE_MODE_MARKDOWN); $config->useReactFileSystem(true); -$bot = new Zanzara(KEY, $config); +$bot = new Zanzara(TELEGRAM_KEY, $config); /* $bot->onUpdate(function (Context $ctx): void { var_dump($ctx->getMessage()->getWebAppData()); diff --git a/mirzaev/arming_bot/system/public/themes/default/css/cart.css b/mirzaev/arming_bot/system/public/themes/default/css/cart.css index 0098164..32f8585 100644 --- a/mirzaev/arming_bot/system/public/themes/default/css/cart.css +++ b/mirzaev/arming_bot/system/public/themes/default/css/cart.css @@ -1,22 +1,86 @@ @charset "UTF-8"; -main>section:is(#summary, #products) { +main>section:is(#summary, #products, #delivery) { width: var(--width); - display: flex; - flex-direction: column; gap: var(--gap); + overflow: hidden; +} + +main>section#delivery>div.column { + padding: 1rem; + gap: var(--gap); + background-color: var(--tg-theme-secondary-bg-color); +} + +main>section#delivery>div.column>div#deliveries>input { + display: none; +} + +main>section#delivery>div.column>div#deliveries>input+label { + /* backdrop-filter: brightness(1.2); */ + /* background-color: unset; */ +} + +main>section#delivery>div.column>div#deliveries>input+label:is(:hover, :focus) { + filter: brightness(1.1) hue-rotate(30deg); + /* filter: unset; */ + /* backdrop-filter: brightness(1.4); */ +} + +main>section#delivery>div.column>div#deliveries>input+label:active { + filter: unset; + /* backdrop-filter: brightness(0.8); */ +} + +main>section#delivery>div.column>div#deliveries>input:checked+label { + filter: hue-rotate(30deg); +} + +main>section#delivery>div.column>div#deliveries>input:checked+label:is(:hover, :focus) { + filter: brightness(1.1) hue-rotate(30deg); +} + +main>section#delivery>div.column>div#deliveries>input:checked+label:active { + filter: brightness(0.9) hue-rotate(30deg); +} + +main>section#delivery>div.column>div#address { + flex-flow: row wrap; + gap: 0.7rem; +} + +main>section#delivery>div.column>div#address>input#city { + min-width: max(6rem, 20%); + width: min(6rem, 100%); + flex-grow: 1; +} + +main>section#delivery>div.column>div#address>input#street { + min-width: max(10rem, 30%); + width: min(10rem, 100%); + flex-grow: 10; +} + +main:has(section#summary.disabled:hover)>section#delivery>div { + filter: brightness(1.2); } main>section#summary>div { + container-type: inline-size; + container-name: summary; padding-left: 1rem; - display: flex; align-items: center; gap: 0.4rem; overflow: hidden; + pointer-events: auto; border-radius: 1.375rem; background-color: var(--tg-theme-secondary-bg-color); } +main>section#summary>div>span { + flex-shrink: 0; +} + main>section#summary>div>span:first-of-type { /* margin-left: auto; */ } @@ -27,6 +91,19 @@ main>section#summary>div>button#order { padding-left: 0.7rem; } +main>section#summary.disabled, +main>section#summary>div:has(button#order:disabled), +main:not(:has(section#delivery>div.column>div#deliveries>input:checked))>section#summary>div:has(button#order:enabled) { + cursor: not-allowed; +} + +main>section#summary.disabled, +main>section#summary>div>button#order:disabled, +main:not(:has(section#delivery>div.column>div#deliveries>input:checked))>section#summary>div>button#order:enabled { + pointer-events: none; + filter: grayscale(1); +} + main>section#products>article.product { position: relative; width: 100%; @@ -137,3 +214,9 @@ main>section#products>article.product>div>div.footer>input { padding: 0 0.3rem; text-align: center; } + +@container summary (max-width: 350px) { + main>section#summary>div>span:not(#cost) { + display: none; + } +} diff --git a/mirzaev/arming_bot/system/public/themes/default/css/main.css b/mirzaev/arming_bot/system/public/themes/default/css/main.css index 0eea4ea..b8def73 100755 --- a/mirzaev/arming_bot/system/public/themes/default/css/main.css +++ b/mirzaev/arming_bot/system/public/themes/default/css/main.css @@ -180,6 +180,20 @@ input { margin-left: var(--currency-offset, 0.1rem); } +.rounded { + border-radius: 0.75rem; +} + +.row { + display: flex; + flex-direction: row; +} + +.column { + display: flex; + flex-direction: column; +} + .unselectable { -webkit-touch-callout: none; -webkit-user-select: none; diff --git a/mirzaev/arming_bot/system/public/themes/default/css/menu.css b/mirzaev/arming_bot/system/public/themes/default/css/menu.css index 40b3a72..6b1fe6b 100755 --- a/mirzaev/arming_bot/system/public/themes/default/css/menu.css +++ b/mirzaev/arming_bot/system/public/themes/default/css/menu.css @@ -6,7 +6,7 @@ header>nav#menu { margin-bottom: 1rem; width: var(--width); min-height: 3rem; - display: flex; + display: flex; flex-flow: row wrap; gap: 1rem; border-radius: 1.375rem; @@ -16,9 +16,7 @@ header>nav#menu { header>nav#menu>a[type="button"] { height: 3rem; padding: unset; - border-radius: 1.375rem; - /* color: var(--unsafe-color, var(--tg-theme-button-text-color)); - background-color: var(--unsafe-background-color, var(--tg-theme-button-color)); */ + border-radius: 1.375rem; color: var(--tg-theme-button-text-color); background-color: var(--tg-theme-button-color); } @@ -32,25 +30,25 @@ header>nav#menu>a[type="button"]>* { } @container header (max-width: 450px) { - header>nav#menu > a[type="button"]:nth-child(1)> i.icon+span { + header>nav#menu>a[type="button"]:nth-child(1)>i.icon+span { display: none; } } @container header (max-width: 350px) { - header>nav#menu > a[type="button"]:nth-child(2)> i.icon+span { + header>nav#menu>a[type="button"]:nth-child(2)>i.icon+span { display: none; } } @container header (max-width: 250px) { - header>nav#menu > a[type="button"]:nth-child(3)> i.icon+span { + header>nav#menu>a[type="button"]:nth-child(3)>i.icon+span { display: none; } } @container header (max-width: 150px) { - header>nav#menu > a[type="button"]> i.icon+span { + header>nav#menu>a[type="button"]>i.icon+span { display: none; } } diff --git a/mirzaev/arming_bot/system/public/themes/default/css/window.css b/mirzaev/arming_bot/system/public/themes/default/css/window.css index 9a375e5..200f463 100755 --- a/mirzaev/arming_bot/system/public/themes/default/css/window.css +++ b/mirzaev/arming_bot/system/public/themes/default/css/window.css @@ -133,15 +133,17 @@ section#window>div.card>p:last-of-type { } section#window>div.card>div.footer { + container-type: inline-size; + container-name: window-footer; + padding: 0.8rem 2.3rem; display: flex; flex-flow: row wrap; align-items: baseline; - gap: 0 0.8rem; + gap: 0.8rem; background-color: var(--tg-theme-header-bg-color); } section#window>div.card>div.footer>small.dimensions { - margin-left: 1.5rem; color: var(--tg-theme-section-header-text-color); } @@ -151,7 +153,6 @@ section#window>div.card>div.footer>small.weight { section#window>div.card>div.footer>p.cost { margin-left: auto; - margin-right: 1.5rem; font-weight: bold; } @@ -159,3 +160,20 @@ section#window>div.card>div.footer>button.buy { width: 100%; height: 3.5rem; } + +@container window-footer (max-width: 350px) { + + section#window>div.card>div.footer>small:first-of-type { + margin-left: auto; + } + + section#window>div.card>div.footer>small:last-of-type { + margin-right: auto; + } + + section#window>div.card>div.footer>p.cost { + margin: unset; + width: 100%; + text-align: center; + } +} diff --git a/mirzaev/arming_bot/system/settings/cdek.php.sample b/mirzaev/arming_bot/system/settings/cdek.php.sample new file mode 100755 index 0000000..18f505e --- /dev/null +++ b/mirzaev/arming_bot/system/settings/cdek.php.sample @@ -0,0 +1,6 @@ +<?php + +return [ + 'account' => '', + 'secret' => '' +]; diff --git a/mirzaev/arming_bot/system/settings/key.php.sample b/mirzaev/arming_bot/system/settings/telegram.php.sample similarity index 100% rename from mirzaev/arming_bot/system/settings/key.php.sample rename to mirzaev/arming_bot/system/settings/telegram.php.sample diff --git a/mirzaev/arming_bot/system/views/templater.php b/mirzaev/arming_bot/system/views/templater.php index bb62902..b20cbd9 100755 --- a/mirzaev/arming_bot/system/views/templater.php +++ b/mirzaev/arming_bot/system/views/templater.php @@ -8,7 +8,6 @@ namespace mirzaev\arming_bot\views; use mirzaev\arming_bot\models\session, mirzaev\arming_bot\models\account, mirzaev\arming_bot\models\settings, - mirzaev\arming_bot\models\cart, mirzaev\arming_bot\models\enumerations\language, mirzaev\arming_bot\models\enumerations\currency; @@ -50,7 +49,6 @@ final class templater extends controller implements ArrayAccess * @param session|null $session The object implementing a session instance from ArangoDB * @param account|null $account The object implementing an account instance from ArangoDB * @param settings|null $settings The object implementing a settings instance from ArangoDB - * @param cart|null $cart The object implementing a cart instance from ArangoDB * * @return void */ @@ -58,7 +56,6 @@ final class templater extends controller implements ArrayAccess ?session $session = null, ?account $account = null, ?settings $settings = null, - ?cart $cart = null ) { // Initializing an instance of twig $this->twig = new twig(new FilesystemLoader(VIEWS)); @@ -75,7 +72,8 @@ final class templater extends controller implements ArrayAccess if (!empty($account?->status())) $this->twig->addGlobal('account', $account); $this->twig->addGlobal('language', $language = $account?->language ?? $session?->buffer['language'] ?? $settings?->language ?? language::en); $this->twig->addGlobal('currency', $currency = $account?->currency ?? $session?->buffer['currency'] ?? $settings?->currency ?? currency::usd); - $this->twig->addGlobal('cart', ['summary' => $cart->summary(currency: $currency), 'products' => $cart->products(language: $language, currency: $currency)]); + + // @todo move functions into controllers // Initialize function of dimensions formatting $this->twig->addFunction( diff --git a/mirzaev/arming_bot/system/views/themes/default/account.html b/mirzaev/arming_bot/system/views/themes/default/account.html index 8a7c5d8..3948324 100755 --- a/mirzaev/arming_bot/system/views/themes/default/account.html +++ b/mirzaev/arming_bot/system/views/themes/default/account.html @@ -6,7 +6,7 @@ {% if account is empty %} <section id="account"> <a onclick="core.account.authentication()"> - <!-- {{ language == 'ru' ? "Аутентификация" : "Authentication" }} --> + {{ language == 'ru' ? "Аутентификация" : "Authentication" }} </a> </section> {% else %} @@ -19,5 +19,5 @@ {% endblock %} {% block js %} -<script src="/js/account.js"></script> +<script src="/js/modules/account.js" type="module"></script> {% endblock %} diff --git a/mirzaev/arming_bot/system/views/themes/default/cart/elements/categories.html b/mirzaev/arming_bot/system/views/themes/default/cart/elements/categories.html deleted file mode 100755 index cb40127..0000000 --- a/mirzaev/arming_bot/system/views/themes/default/cart/elements/categories.html +++ /dev/null @@ -1,5 +0,0 @@ -{% if ASDASDASDASDASDASDSD is not empty %} -<section id="" class="unselectable"> - -</section> -{% endif %} diff --git a/mirzaev/arming_bot/system/views/themes/default/cart/elements/delivery.html b/mirzaev/arming_bot/system/views/themes/default/cart/elements/delivery.html new file mode 100755 index 0000000..7982faa --- /dev/null +++ b/mirzaev/arming_bot/system/views/themes/default/cart/elements/delivery.html @@ -0,0 +1,25 @@ +{% if deliveries is not empty %} +<section id="delivery" class="rounded column unselectable"> + <div class="column"> + <div id="deliveries" class="row"> + {% for identifier, delivery in deliveries %} + <input id="{{ identifier }}" name="delivery" type="radio" {% if identifier==session.buffer.delivery.company %} + checked{% endif %}> + <label class="rounded" for="{{ identifier }}" type="button" + title="{{ (language.name == 'ru' ? 'Выбрать ' : 'Choose ') ~ delivery.label }}" + onclick="core.buffer.write.damper('delivery_company', '{{ identifier }}').then(() => core.delivery.check())" tabindex="5"> + {{ delivery.label }} + </label> + {% endfor %} + </div> + <div id="address" class="row"> + <input id="city" placeholder="{{ language.name == 'ru' ? 'Город' : 'City' }}" + value="{{ session.buffer.delivery.city ?? account.buffer.delivery.city }}" + onkeydown="core.buffer.write.damper('delivery_city', this.value).then(() => core.delivery.check())" tabindex="6" /> + <input id="street" placeholder="{{ language.name == 'ru' ? 'Улица' : 'Street' }}" + value="{{ session.buffer.delivery.street ?? session.buffer.delivery.street }}" + onkeydown="core.buffer.write.damper('delivery_street', this.value).then(() => core.delivery.check())" tabindex="6" /> + </div> + </div> +</section> +{% endif %} diff --git a/mirzaev/arming_bot/system/views/themes/default/cart/elements/products.html b/mirzaev/arming_bot/system/views/themes/default/cart/elements/products.html index af4ae4d..25a215e 100755 --- a/mirzaev/arming_bot/system/views/themes/default/cart/elements/products.html +++ b/mirzaev/arming_bot/system/views/themes/default/cart/elements/products.html @@ -1,5 +1,5 @@ {% macro card(product, amount) %} -<article id="{{ product._id }}" class="product unselectable" data-product-identifier="{{ product.identifier }}" +<article id="{{ product._id }}" class="product" data-product-identifier="{{ product.identifier }}" data-product-amount="{{ amount }}" {% if amount> 0 %} style="--hue-rotate-offset: {{ amount }}0deg;"{% endif %}> <a data-product="cover" href="?product={{ product.identifier }}" onclick="return core.catalog.product(this);" onkeydown="event.keyCode === 13 && core.catalog.product(this)" tabindex="10"> @@ -40,8 +40,9 @@ </article> {% endmacro %} {% if cart.products is not empty %} -<section id="products" class="unselectable"> +<section id="products" class="column unselectable"> {% for product in cart.products %} + {{ product.summary }} {{ _self.card(product.document, product.amount) }} {% endfor %} </section> diff --git a/mirzaev/arming_bot/system/views/themes/default/cart/elements/summary.html b/mirzaev/arming_bot/system/views/themes/default/cart/elements/summary.html index 4b8a779..8dd7c47 100755 --- a/mirzaev/arming_bot/system/views/themes/default/cart/elements/summary.html +++ b/mirzaev/arming_bot/system/views/themes/default/cart/elements/summary.html @@ -1,10 +1,13 @@ {% if cart.products is not empty %} -<section id="summary" class="unselectable"> - <div> +<section id="summary" + class="column {% if delivery is empty or delivery.city is empty or delivery.street is empty %}disabled{% endif %} unselectable" + {% if delivery is empty %}title="{{ language.name == " ru" ? "Выберите тип доставки" : "Choose delivery type" }}"{% + endif %}> + <div class="row"> <span id="amount">{{ cart.summary.amount ?? 0 }}</span> <span>{{ language.name == "ru" ? "товаров на сумму" : "products worth" }}</span> <span id="cost" class="cost currency">{{ cart.summary.cost ?? 0 }}</span> - <button id="order" onclick="core.cart.order(this)" + <button id="order" onclick="core.cart.order(this)" {% if delivery is empty %} disabled="true" {% endif %} title="{{ language.name == 'ru' ? 'Оформить заказ' : 'Place an order' }}"><i class="icon arrow"></i></button> </div> </section> diff --git a/mirzaev/arming_bot/system/views/themes/default/cart/page.html b/mirzaev/arming_bot/system/views/themes/default/cart/page.html index 02d806a..8ee16e2 100755 --- a/mirzaev/arming_bot/system/views/themes/default/cart/page.html +++ b/mirzaev/arming_bot/system/views/themes/default/cart/page.html @@ -12,13 +12,15 @@ {% endblock %} {% block main %} -<h2 class="unselectable">{{ h2 }}</h2> +<h2 id="title" class="unselectable">{{ h2 }}</h2> +{% include "/themes/default/cart/elements/delivery.html" %} {% include "/themes/default/cart/elements/summary.html" %} {% include "/themes/default/cart/elements/products.html" %} {% endblock %} {% block js %} {{ parent() }} -<script src="/js/cart.js"></script> -<script src="/js/hotline.js"></script> +<script src="/js/modules/delivery.js" type="module"></script> +<script src="/js/modules/cart.js" type="module"></script> +<script src="/js/modules/hotline.js" type="module"></script> {% endblock %} diff --git a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/filters.html b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/filters.html index 85da2da..cae919b 100755 --- a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/filters.html +++ b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/filters.html @@ -1,7 +1,7 @@ {% if filters is not empty %} <section id="filters" class="unselectble"> <section class="unselectable" data-type="select" tabindex="4"> - {% set buffer_brand = account.buffer.catalog.filters.brand ?? session.buffer.catalog.filters.brand %} + {% set buffer_brand = session.buffer.catalog.filters.brand ?? account.buffer.catalog.filters.brand %} <input name="brand" type="radio" id="brand_title" {% if buffer_brand is empty %} checked{% endif %}> <label for="brand_title" type="button" onclick="core.catalog.parameters.set('brand', null); core.catalog.search(event)"> {{ language.name == 'ru' ? 'Бренд' : 'Brand' }} diff --git a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products.html b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products.html index 8fc7089..e3b23ce 100755 --- a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products.html +++ b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/products.html @@ -16,7 +16,6 @@ product.dimensions.y, product.dimensions.z, ' ') ~ ' ' ~ product.weight ~ 'г' % <button data-product-button="toggle" onclick="core.cart.toggle(this, document.getElementById('{{ product.getId() }}'))" tabindex="15"> <span data-product-parameter="amount">{{ amount }}</span> <span class="cost currency" data-product-parameter="cost">{{ product.cost }}</span> - <span data-product-parameter="currency">{{ currency.symbol }}</span> </button> <button data-product-button="write" onclick="core.cart.write(this, document.getElementById('{{ product.getId() }}'), 1)" title="{{ language.name == 'ru' ? 'Увеличить' : 'Increase' }}"><i class="icon small plus"></i></button> </div> diff --git a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/search.html b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/search.html index 29b004a..2f2d8bb 100755 --- a/mirzaev/arming_bot/system/views/themes/default/catalog/elements/search.html +++ b/mirzaev/arming_bot/system/views/themes/default/catalog/elements/search.html @@ -1,6 +1,6 @@ <search id="search"> <label class="unselectable"><i class="icon search"></i></label> - {% set buffer_search = account.buffer.catalog.search.text ?? session.buffer.catalog.search.text %} + {% set buffer_search = session.buffer.catalog.search.text ?? account.buffer.catalog.search.text %} <input placeholder="{{ language.name == 'ru' ? 'Поиск по каталогу' : 'Search in the catalog' }}" type="search" tabindex="1" onkeyup="event.keyCode === 9 || core.catalog.parameters.set('text', this.value); core.catalog.search(event, this)" {% if buffer_search is not empty %} value="{{ buffer_search }}" {% endif %} /> diff --git a/mirzaev/arming_bot/system/views/themes/default/catalog/page.html b/mirzaev/arming_bot/system/views/themes/default/catalog/page.html index d393f6f..c91c7bd 100755 --- a/mirzaev/arming_bot/system/views/themes/default/catalog/page.html +++ b/mirzaev/arming_bot/system/views/themes/default/catalog/page.html @@ -12,7 +12,7 @@ {% endblock %} {% block main %} -<h2 class="unselectable">{{ h2 }}</h2> +<h2 id="title" class="unselectable">{{ h2 }}</h2> {% include "/themes/default/catalog/elements/search.html" %} {% include "/themes/default/catalog/elements/categories.html" %} {% include "/themes/default/catalog/elements/filters.html" %} @@ -21,7 +21,7 @@ {% block js %} {{ parent() }} -<script src="/js/catalog.js"></script> -<script src="/js/cart.js"></script> -<script src="/js/hotline.js"></script> +<script src="/js/modules/catalog.js" type="module"></script> +<script src="/js/modules/cart.js" type="module"></script> +<script src="/js/hotline.js" type="module"></script> {% endblock %} diff --git a/mirzaev/arming_bot/system/views/themes/default/connection.html b/mirzaev/arming_bot/system/views/themes/default/connection.html index 9fd1007..53f3816 100755 --- a/mirzaev/arming_bot/system/views/themes/default/connection.html +++ b/mirzaev/arming_bot/system/views/themes/default/connection.html @@ -10,5 +10,5 @@ {% endblock %} {% block js %} -<script src="/js/connection.js"></script> +<script src="/js/modules/connection.js" type="module"></script> {% endblock %} diff --git a/mirzaev/arming_bot/system/views/themes/default/js.html b/mirzaev/arming_bot/system/views/themes/default/js.html index cabba8e..8b05cb3 100755 --- a/mirzaev/arming_bot/system/views/themes/default/js.html +++ b/mirzaev/arming_bot/system/views/themes/default/js.html @@ -1,38 +1,20 @@ {% block js %} <script src="https://telegram.org/js/telegram-web-app.js"></script> +<script src="/js/modules/damper.js" type="module"></script> <script src="/js/core.js"></script> -<script src="/js/damper.js"></script> -<script src="/js/loader.js"></script> -<script src="/js/telegram.js"></script> -<script src="/js/session.js"></script> +<script src="/js/modules/loader.js" type="module"></script> +<script src="/js/modules/telegram.js" type="module"></script> +<script src="/js/modules/session.js" type="module"></script> <script src="/js/authentication.js"></script> {% if javascript is not empty %} <script> -import("/js/core.js").then(() => - import("/js/damper.js").then(() => { - const dependencies = setInterval(() => { - if ( - typeof core === "function" && - typeof core.damper === "function" - ) { - clearInterval(dependencies); - clearTimeout(timeout); - initialization(); - } - }, 10); - const timeout = setTimeout(() => { - clearInterval(dependencies); - initialization(); - }, 5000); +core.modules.connect("damper").then(() => { + let _window; - function initialization() { - let _window; - {% for code in javascript %} - {{ code|raw }} - {% endfor %} - } - }) -); + {% for code in javascript %} + {{ code|raw }} + {% endfor %} +}); </script> {% endif %} {% endblock %} diff --git a/mirzaev/arming_bot/system/views/themes/default/menu.html b/mirzaev/arming_bot/system/views/themes/default/menu.html index 6323734..bdbf8ca 100644 --- a/mirzaev/arming_bot/system/views/themes/default/menu.html +++ b/mirzaev/arming_bot/system/views/themes/default/menu.html @@ -12,7 +12,7 @@ {% for button in menu %} <a href='{{ button.urn }}' onclick="return core.loader.load('{{ button.urn }}');" type="button" class="unselectable" title="{{ button.name }}" {% if button.style %} - style="{% for parameter, value in button.style %}{{ parameter ~ ': ' ~ value ~ '; ' }}{% endfor %}" {% endif %}"> + style="{% for parameter, value in button.style %}{{ parameter ~ ': ' ~ value ~ '; ' }}{% endfor %}" {% endif %}> {% if button.icon %} <i class="icon {{ button.icon.class }}" {% if button.icon.style %} style="{% for parameter, value in button.icon.style %}{{ parameter ~ ': ' ~ value ~ '; ' }}{% endfor %}" {% endif diff --git a/mirzaev/arming_bot/system/views/themes/default/orcer/elements/delivery.html b/mirzaev/arming_bot/system/views/themes/default/orcer/elements/delivery.html new file mode 100755 index 0000000..6fbd88e --- /dev/null +++ b/mirzaev/arming_bot/system/views/themes/default/orcer/elements/delivery.html @@ -0,0 +1,11 @@ +{% if deliveries is not empty %} +<section id="deliveries" class="unselectable"> + {% for identifier, delivery in deliveries %} + <div id="{{ identifier }}"> + <h4>{{ identifier[:1]|upper ~ identifier[1:] }}</h4> + <button onclick="core.delivery.choose(this, '{{ identifier }}')" + title="{{ language.name == 'ru' ? 'Выбрать {{ identifier }}' : 'Choose {{ identifier }}' }}"></button> + </div> + {% endfor %} +</section> +{% endif %} diff --git a/mirzaev/arming_bot/system/views/themes/default/orcer/elements/summary.html b/mirzaev/arming_bot/system/views/themes/default/orcer/elements/summary.html new file mode 100755 index 0000000..4b8a779 --- /dev/null +++ b/mirzaev/arming_bot/system/views/themes/default/orcer/elements/summary.html @@ -0,0 +1,11 @@ +{% if cart.products is not empty %} +<section id="summary" class="unselectable"> + <div> + <span id="amount">{{ cart.summary.amount ?? 0 }}</span> + <span>{{ language.name == "ru" ? "товаров на сумму" : "products worth" }}</span> + <span id="cost" class="cost currency">{{ cart.summary.cost ?? 0 }}</span> + <button id="order" onclick="core.cart.order(this)" + title="{{ language.name == 'ru' ? 'Оформить заказ' : 'Place an order' }}"><i class="icon arrow"></i></button> + </div> +</section> +{% endif %} diff --git a/mirzaev/arming_bot/system/views/themes/default/orcer/page.html b/mirzaev/arming_bot/system/views/themes/default/orcer/page.html new file mode 100755 index 0000000..fce98fc --- /dev/null +++ b/mirzaev/arming_bot/system/views/themes/default/orcer/page.html @@ -0,0 +1,24 @@ +{% extends "/themes/default/index.html" %} + +{% block css %} +{{ parent() }} +<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/cart.css" /> +<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/trash.css" /> +<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/list_add.css" /> +<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/shopping_cart.css" /> +<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/plus.css" /> +<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/minus.css" /> +<link type="text/css" rel="stylesheet" href="/themes/{{ theme }}/css/icons/arrow.css" /> +{% endblock %} + +{% block main %} +<h2 id="title" class="unselectable">{{ h2 }}</h2> +{% include "/themes/default/cart/elements/delivery.html" %} +{% include "/themes/default/cart/elements/summary.html" %} +{% endblock %} + +{% block js %} +{{ parent() }} +<script src="/js/modules/cart.js" type="module"></script> +<script src="/js/modules/hotline.js" type="module"></script> +{% endblock %}