generated from mirzaev/pot-php
PRINT TO A6 PAPER
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
"name": "Arsen Mirzaev Tatyano-Muradovich",
|
"name": "Arsen Mirzaev Tatyano-Muradovich",
|
||||||
"email": "arsen@mirzaev.sexy",
|
"email": "arsen@mirzaev.sexy",
|
||||||
"homepage": "https://mirzaev.sexy",
|
"homepage": "https://mirzaev.sexy",
|
||||||
"role": "Creator"
|
"role": "Developer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
@@ -23,9 +23,10 @@
|
|||||||
"issues": "https://git.svoboda.works/svoboda/pechatalka/issues"
|
"issues": "https://git.svoboda.works/svoboda/pechatalka/issues"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.4",
|
"php": "^8.5",
|
||||||
"mirzaev/minimal": "^3",
|
"ext-imagick": "^3.8",
|
||||||
"mirzaev/baza": "^3.3",
|
"mirzaev/minimal": "^3.10",
|
||||||
|
"mirzaev/baza": "^3.4.3",
|
||||||
"twig/twig": "^3.2",
|
"twig/twig": "^3.2",
|
||||||
"twig/extra-bundle": "^3.7",
|
"twig/extra-bundle": "^3.7",
|
||||||
"twig/intl-extra": "^3.10",
|
"twig/intl-extra": "^3.10",
|
||||||
@@ -33,7 +34,8 @@
|
|||||||
"imagine/imagine": "^1.5",
|
"imagine/imagine": "^1.5",
|
||||||
"badfarm/zanzara": "^0.9.1",
|
"badfarm/zanzara": "^0.9.1",
|
||||||
"nyholm/psr7": "^1.8",
|
"nyholm/psr7": "^1.8",
|
||||||
"react/filesystem": "^0.1.2"
|
"react/filesystem": "^0.1.2",
|
||||||
|
"mirzaev/record": "^2.2"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|||||||
Submodule damper.mjs updated: 68589e968c...81d208b964
21
install.sh
21
install.sh
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
git submodule update --init --recursive
|
||||||
|
|
||||||
if [ -d author/project ]; then
|
if [ -d author/project ]; then
|
||||||
mv author/project author/pechatalka
|
mv author/project author/pechatalka
|
||||||
fi
|
fi
|
||||||
@@ -8,3 +10,22 @@ if [ -d author ]; then
|
|||||||
mv author mirzaev
|
mv author mirzaev
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
for i in svoboda/pechatalka/system/settings/*.sample; do
|
||||||
|
echo $i;
|
||||||
|
if [ ! -f "${i/.sample/}" ]; then
|
||||||
|
cp -n "$i" "${i/.sample/}";
|
||||||
|
echo ${i/.sample/};
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! [ -d svoboda/pechatalka/system/public/js/modules ]; then
|
||||||
|
mkdir svoboda/pechatalka/system/public/js/modules -p
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -L svoboda/pechatalka/system/public/js/modules/damper.mjs ]; then
|
||||||
|
ln -s ../../../../../../damper.mjs/damper.mjs svoboda/pechatalka/system/public/js/modules/damper.mjs;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -L svoboda/pechatalka/system/public/js/modules/pechatalka.mjs ]; then
|
||||||
|
ln -s ../../../../../../pechatalka.mjs/pechatalka.mjs svoboda/pechatalka/system/public/js/modules/pechatalka.mjs;
|
||||||
|
fi
|
||||||
|
|||||||
Submodule pechatalka.mjs updated: b1400479c4...c94542bfd3
@@ -6,7 +6,9 @@ namespace svoboda\pechatalka\controllers;
|
|||||||
|
|
||||||
// Files of the project
|
// Files of the project
|
||||||
use svoboda\pechatalka\controllers\core,
|
use svoboda\pechatalka\controllers\core,
|
||||||
svoboda\pechatalka\models\enumerations\products\type;
|
svoboda\pechatalka\models\paper,
|
||||||
|
svoboda\pechatalka\models\product,
|
||||||
|
svoboda\pechatalka\models\enumerations\products\type as product_type;
|
||||||
|
|
||||||
// Framework for PHP
|
// Framework for PHP
|
||||||
use mirzaev\minimal\http\enumerations\content,
|
use mirzaev\minimal\http\enumerations\content,
|
||||||
@@ -72,42 +74,127 @@ final class generator extends core
|
|||||||
/**
|
/**
|
||||||
* Print
|
* Print
|
||||||
*
|
*
|
||||||
* Assemble and generate the blank from layers and prepare to printing
|
* Assemble and generate the blank from products and prepare to printing
|
||||||
*
|
*
|
||||||
* @return null
|
* @return null
|
||||||
*/
|
*/
|
||||||
public function print(
|
public function print(
|
||||||
string $type,
|
string $type = '',
|
||||||
string|int $amount = 1,
|
string $products = '',
|
||||||
?string $display
|
?string $canvas = null
|
||||||
): null {
|
): null {
|
||||||
// Type
|
// Type
|
||||||
$type = type::{$type} ?? null;
|
!empty($type) and $type = product_type::{$type} ?? null;
|
||||||
|
|
||||||
// Amount
|
// Amount
|
||||||
preg_match('/[\d]+/', (string) $amount, $matches);
|
/* preg_match('/[\d]+/', (string) $amount, $matches);
|
||||||
$amount = (int) $matches[0] ?? 1;
|
$amount = (int) $matches[0] ?? 1;
|
||||||
unset($matches);
|
unset($matches); */
|
||||||
|
|
||||||
// Display
|
// Initializing products data
|
||||||
$display = json_decode($display, true, 10);
|
$products = json_decode($products, true, 16);
|
||||||
|
|
||||||
if ($type instanceof type && $amount > 0) {
|
// Initializing the canvas data
|
||||||
|
$canvas = json_decode($canvas, true, 16);
|
||||||
|
|
||||||
|
if ($type instanceof product_type && count($products) > 0) {
|
||||||
// Initialized all required arguments
|
// Initialized all required arguments
|
||||||
|
|
||||||
// Universalizing received images
|
// Declaring the images buffer
|
||||||
$files_images = array_filter($this->request->files, fn(string $key) => preg_match('/^layer_\d{1,2}_image/', $key), ARRAY_FILTER_USE_KEY);
|
$images = [];
|
||||||
|
|
||||||
// Initializing the combined images buffer
|
foreach ($this->request->files as $name => $file) {
|
||||||
$images_combined = [];
|
// Iterating over received files
|
||||||
|
|
||||||
for ($i = 0; $i < count($files_images); ++$i) {
|
// Deserializing the file type
|
||||||
// Iterating over images
|
!empty($file['type']) && is_string($file['type']) and $file['type'] = content::from($file['type']) ?? null;
|
||||||
|
|
||||||
// Combining images with sorting
|
if (match ($file['type']) {
|
||||||
if (empty($display[$i]['image'])) $images_combined[] = $display[$i]['image'];
|
content::jpeg,
|
||||||
if (!empty($files_images["image_$i"])) $images_combined[] = $files_images["image_$i"];
|
content::png,
|
||||||
if (empty($display[$i]['image'])) $display[$i]['image'] = $files_images[$i];
|
content::webp => true,
|
||||||
|
default => false
|
||||||
|
}) {
|
||||||
|
// Passed the file type filter
|
||||||
|
|
||||||
|
// Writing into the images buffer
|
||||||
|
$images[$name] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($products as $product => &$layers) {
|
||||||
|
// Iterating over products
|
||||||
|
|
||||||
|
// Creating the product
|
||||||
|
$instance = new product()->create(
|
||||||
|
account: 0,
|
||||||
|
type: $type,
|
||||||
|
layers: $layers,
|
||||||
|
canvas: $canvas
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initializing the product storage path
|
||||||
|
$storage = STORAGE
|
||||||
|
. DIRECTORY_SEPARATOR
|
||||||
|
. 'products'
|
||||||
|
. DIRECTORY_SEPARATOR
|
||||||
|
. $instance->identifier;
|
||||||
|
|
||||||
|
if (!file_exists(filename: $storage)) {
|
||||||
|
// The product storage directory is not created
|
||||||
|
|
||||||
|
if (mkdir(directory: $storage, permissions: 0755, recursive: true)) {
|
||||||
|
// Created the product storage directory
|
||||||
|
} else {
|
||||||
|
// Nor created the product storage directory
|
||||||
|
|
||||||
|
// Exit (fail)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_filter($images, fn(string $key) => preg_match('/^' . $product . '_\d{1,2}$/', $key), ARRAY_FILTER_USE_KEY) as $name => $image) {
|
||||||
|
// Iterating over the product images
|
||||||
|
|
||||||
|
// Initializing the layer
|
||||||
|
preg_match('/\d{1,2}$/', $name, $matches);
|
||||||
|
$layer = $matches[0];
|
||||||
|
|
||||||
|
if (!empty($layer) || $layer == 0) {
|
||||||
|
// Initialized the layer
|
||||||
|
|
||||||
|
// Initializing the product layer image file path
|
||||||
|
$path = $storage . DIRECTORY_SEPARATOR . $layer . '.' . $image['type']->extension();
|
||||||
|
|
||||||
|
if (copy(from: $image['tmp_name'], to: $path)) {
|
||||||
|
// Copied the product layer image into the product storage
|
||||||
|
|
||||||
|
// Matching the product image with the product
|
||||||
|
$layers[$layer]['image'] = $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($products as $product) {
|
||||||
|
// Iterating over products
|
||||||
|
|
||||||
|
foreach ($product as $layer) {
|
||||||
|
// Iterating over the product layer
|
||||||
|
|
||||||
|
if (empty($layer['image'])) {
|
||||||
|
// Found the empty layer
|
||||||
|
|
||||||
|
// Exit (fail)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type === product_type::pin_37) {
|
||||||
|
// Pin 37mm
|
||||||
|
|
||||||
|
$biba = paper::a5(type: $type, products: $products, canvas: $canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str_contains($this->request->headers['accept'], content::any->value)) {
|
if (str_contains($this->request->headers['accept'], content::any->value)) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ final class index extends core
|
|||||||
*/
|
*/
|
||||||
public function index(): null
|
public function index(): null
|
||||||
{
|
{
|
||||||
if (str_contains($this->request->headers['accept'], content::any->value)) {
|
if (str_contains($this->request->headers['accept'] ?? '', content::any->value)) {
|
||||||
// Request for any response
|
// Request for any response
|
||||||
|
|
||||||
// Render page
|
// Render page
|
||||||
|
|||||||
0
svoboda/pechatalka/system/databases/.gitignore
vendored
Normal file → Executable file
0
svoboda/pechatalka/system/databases/.gitignore
vendored
Normal file → Executable file
@@ -15,5 +15,25 @@ namespace svoboda\pechatalka\models\enumerations\products;
|
|||||||
enum type
|
enum type
|
||||||
{
|
{
|
||||||
case pin_37;
|
case pin_37;
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Areas
|
||||||
|
*
|
||||||
|
* @return array ['visible' => ['width', 'height'], 'blank' => ['width', 'height']]
|
||||||
|
*/
|
||||||
|
public function areas(): array
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
type::pin_37 => [
|
||||||
|
'visible' => [
|
||||||
|
'width' => 37,
|
||||||
|
'height' => 37
|
||||||
|
],
|
||||||
|
'blank' => [
|
||||||
|
'width' => 48.5,
|
||||||
|
'height' => 48.5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
158
svoboda/pechatalka/system/models/paper.php
Normal file
158
svoboda/pechatalka/system/models/paper.php
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace svoboda\pechatalka\models;
|
||||||
|
|
||||||
|
// Files of the project
|
||||||
|
use svoboda\pechatalka\models\core,
|
||||||
|
svoboda\pechatalka\models\enumerations\products\type as product_type;
|
||||||
|
|
||||||
|
// Svoboda time
|
||||||
|
use svoboda\time\statement as svoboda;
|
||||||
|
|
||||||
|
// Baza database
|
||||||
|
use mirzaev\baza\database,
|
||||||
|
mirzaev\baza\column,
|
||||||
|
mirzaev\baza\record,
|
||||||
|
mirzaev\baza\enumerations\encoding,
|
||||||
|
mirzaev\baza\enumerations\type;
|
||||||
|
|
||||||
|
// Framework for Telegram
|
||||||
|
use Zanzara\Telegram\Type\User as telegram;
|
||||||
|
|
||||||
|
// Built-in libraries
|
||||||
|
use Imagick as imagick,
|
||||||
|
ImagickPixel as imagick_pixel,
|
||||||
|
ImagickDraw as imagick_draw,
|
||||||
|
Exception as exception,
|
||||||
|
RuntimeException as exception_runtime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Paper
|
||||||
|
*
|
||||||
|
* @package svoboda\pechatalka\models
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class paper extends core
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A5
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return record The account record from the database
|
||||||
|
*/
|
||||||
|
public static function a5(product_type $type, array $products, array $canvas = []): mixed
|
||||||
|
{
|
||||||
|
// Initializing the print DPI
|
||||||
|
define('dpi', 80);
|
||||||
|
/* define('dpi', 300); */
|
||||||
|
|
||||||
|
// Initializing the A5 paper dimensions
|
||||||
|
$a5 = [
|
||||||
|
'width' => (int) round(10.5 * dpi),
|
||||||
|
'height' => (int) round(14.8 * dpi)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initializing the paper
|
||||||
|
$paper = new imagick();
|
||||||
|
$paper->setResolution(dpi, dpi);
|
||||||
|
$paper->newImage($a5['width'], $a5['height'], new imagick_pixel("white"));
|
||||||
|
$paper->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
|
||||||
|
$paper->setImageFormat("jpg");
|
||||||
|
|
||||||
|
foreach ($products as $product) {
|
||||||
|
// Iterating over products
|
||||||
|
|
||||||
|
foreach ($product as $layer) {
|
||||||
|
// Iterating over the product layers
|
||||||
|
|
||||||
|
// Filtering
|
||||||
|
empty($layer['scale']) || $layer['scale'] == 0 and $layer['scale'] = 1;
|
||||||
|
|
||||||
|
// Initializing the product areas
|
||||||
|
$areas = $type->areas();
|
||||||
|
|
||||||
|
// Calculating the product blank dimensions by the print DPI
|
||||||
|
$blank = [
|
||||||
|
'width' => (int) round($areas['blank']['width'] / 10 * dpi),
|
||||||
|
'height' => (int) round($areas['blank']['height'] / 10 * dpi)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Calculating the DPI ratio of the pechatalka interface to the blank dimensions
|
||||||
|
$ratio = [
|
||||||
|
'width' => $blank['width'] / $canvas['width'],
|
||||||
|
'height' => $blank['height'] / $canvas['height']
|
||||||
|
];
|
||||||
|
|
||||||
|
// Initializing the circle mask
|
||||||
|
$circle = new imagick();
|
||||||
|
$circle->setResolution(dpi, dpi);
|
||||||
|
$circle->newImage($blank['width'], $blank['height'], '#0008');
|
||||||
|
$circle->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
|
||||||
|
$circle->setimageformat('png');
|
||||||
|
$circle->setimagematte(true);
|
||||||
|
$draw = new imagick_draw();
|
||||||
|
$draw->setfillcolor('#fff');
|
||||||
|
$draw->circle($blank['width'] / 2, $blank['height'] / 2, $blank['width'] / 2, $blank['height']);
|
||||||
|
$circle->drawimage($draw);
|
||||||
|
|
||||||
|
// Initializing the layer image
|
||||||
|
$image = new imagick();
|
||||||
|
$image->setResolution(dpi, dpi);
|
||||||
|
$image->readImage($layer['image']);
|
||||||
|
$image->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
|
||||||
|
$image->setimagematte(true);
|
||||||
|
|
||||||
|
$width = (int) round($blank['width'] * $layer['scale']);
|
||||||
|
$image->adaptiveResizeImage($width, 0);
|
||||||
|
/* $image->roundCornersImage(50, 50); */
|
||||||
|
|
||||||
|
$offset = [
|
||||||
|
'x' => ($blank['width'] - $image->getImageWidth()) / 2,
|
||||||
|
'y' => ($blank['height'] - $image->getImageHeight()) / 2
|
||||||
|
];
|
||||||
|
|
||||||
|
$vertical = $blank['height'] - $image->getImageHeight();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Добавить нормальные комментарии после глубокго тестирования
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Reinitializing the product layer offsets
|
||||||
|
$layer['x'] = (int) round($layer['x'] * $ratio['width']);
|
||||||
|
$layer['y'] = (int) round($layer['y'] * $ratio['height']);
|
||||||
|
|
||||||
|
$image->compositeImage(
|
||||||
|
$circle,
|
||||||
|
imagick::COMPOSITE_DSTIN,
|
||||||
|
(int) round(-$layer['x'] - $offset['x']),
|
||||||
|
/* (int) round(-$layer['y'] - $offset['y'] - $vertical / 2) */
|
||||||
|
(int) round(-$layer['y'] - $offset['y'])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compositing the layer with the paper
|
||||||
|
$paper->setImageVirtualPixelMethod(imagick::VIRTUALPIXELMETHOD_TRANSPARENT);
|
||||||
|
$paper->setImageArtifact('compose:args', "1,0,-0.5,0.5");
|
||||||
|
$paper->compositeImage(
|
||||||
|
$image,
|
||||||
|
imagick::COMPOSITE_MATHEMATICS,
|
||||||
|
(int) round($a5['width'] / 2 - $blank['width'] / 2 + $layer['x'] + $offset['x']),
|
||||||
|
/* (int) round($a5['height'] / 2 - $blank['height'] / 2 + $layer['y'] + $offset['y'] + $vertical / 2) */
|
||||||
|
(int) round($a5['height'] / 2 - $blank['height'] / 2 + $layer['y'] + $offset['y'])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing the paper file
|
||||||
|
$paper->writeImage(INDEX . DIRECTORY_SEPARATOR . 'test.jpg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
132
svoboda/pechatalka/system/models/product.php
Normal file
132
svoboda/pechatalka/system/models/product.php
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace svoboda\pechatalka\models;
|
||||||
|
|
||||||
|
// Files of the project
|
||||||
|
use svoboda\pechatalka\models\core,
|
||||||
|
svoboda\pechatalka\models\enumerations\products\type as product_type;
|
||||||
|
|
||||||
|
// Svoboda time
|
||||||
|
use svoboda\time\statement as svoboda;
|
||||||
|
|
||||||
|
// Baza database
|
||||||
|
use mirzaev\baza\database,
|
||||||
|
mirzaev\baza\column,
|
||||||
|
mirzaev\baza\record,
|
||||||
|
mirzaev\baza\enumerations\encoding,
|
||||||
|
mirzaev\baza\enumerations\type;
|
||||||
|
|
||||||
|
// Active Record pattern
|
||||||
|
use mirzaev\record\interfaces\record as record_interface,
|
||||||
|
mirzaev\record\traits\record as record_trait;
|
||||||
|
|
||||||
|
|
||||||
|
// Framework for Telegram
|
||||||
|
use Zanzara\Telegram\Type\User as telegram;
|
||||||
|
|
||||||
|
// Built-in libraries
|
||||||
|
use Exception as exception,
|
||||||
|
RuntimeException as exception_runtime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Product
|
||||||
|
*
|
||||||
|
* @package svoboda\pechatalka\models
|
||||||
|
*
|
||||||
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
||||||
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
|
*/
|
||||||
|
final class product extends core implements record_interface
|
||||||
|
{
|
||||||
|
use record_trait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File
|
||||||
|
*
|
||||||
|
* @var string $database Path to the database file
|
||||||
|
*/
|
||||||
|
protected string $file = DATABASES . DIRECTORY_SEPARATOR . 'products.baza';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database
|
||||||
|
*
|
||||||
|
* @method record|null $record The record
|
||||||
|
*
|
||||||
|
* @var database $database The database
|
||||||
|
*/
|
||||||
|
public protected(set) database $database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized
|
||||||
|
*
|
||||||
|
* @var bool $serialized Is the implementator object serialized?
|
||||||
|
*/
|
||||||
|
private bool $serialized = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(?record $record = null)
|
||||||
|
{
|
||||||
|
// Initializing the database
|
||||||
|
$this->database = new database()
|
||||||
|
->encoding(encoding::utf8)
|
||||||
|
->columns(
|
||||||
|
new column('identifier', type::integer_unsigned),
|
||||||
|
new column('account', type::integer_unsigned),
|
||||||
|
new column('type', type::string, ['length' => 32]),
|
||||||
|
new column('layers', type::string, ['length' => 4096]),
|
||||||
|
new column('canvas', type::string, ['length' => 4096]),
|
||||||
|
new column('updated', type::integer_unsigned),
|
||||||
|
new column('created', type::integer_unsigned)
|
||||||
|
)
|
||||||
|
->connect($this->file);
|
||||||
|
|
||||||
|
// Initializing the record
|
||||||
|
$record instanceof record and $this->record = $record;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create
|
||||||
|
*
|
||||||
|
* @param int $account The account identifier
|
||||||
|
* @param string|product_type $type The product type
|
||||||
|
* @param string|array $layers The product layers (JSON)
|
||||||
|
* @param string|array $canvas The product canvas (JSON)
|
||||||
|
*
|
||||||
|
* @return int|false The instance, if created
|
||||||
|
*/
|
||||||
|
public function create(
|
||||||
|
int $account,
|
||||||
|
string|product_type $type,
|
||||||
|
string|array $layers,
|
||||||
|
string|array|null $canvas = null
|
||||||
|
): static|false
|
||||||
|
{
|
||||||
|
// Initializing the identifier
|
||||||
|
$identifier = $this->database->count() + 1;
|
||||||
|
|
||||||
|
// Initializing the record
|
||||||
|
$record = $this->database->record(
|
||||||
|
$identifier,
|
||||||
|
$account,
|
||||||
|
$type instanceof product_type ? $type->name : $type,
|
||||||
|
is_string($layers) && json_validate($layers, depth: 16) ? $layers : json_encode($layers, depth: 16),
|
||||||
|
is_string($canvas) && json_validate($canvas, depth: 16) ? $canvas : json_encode($canvas, depth: 16),
|
||||||
|
svoboda::timestamp(),
|
||||||
|
svoboda::timestamp()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Writing the record into the database
|
||||||
|
$created = $this->database->write($record);
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return $record ? new static(record: $record) : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ $core = new core(namespace: __NAMESPACE__);
|
|||||||
// Initializing routes
|
// Initializing routes
|
||||||
$core->router
|
$core->router
|
||||||
->write('/', new route('index', 'index'), 'GET')
|
->write('/', new route('index', 'index'), 'GET')
|
||||||
->write('/generator/print', new route('generator', 'print'), 'POST')
|
->write('/generator/print', new route('generator', 'print', options: ['controller_method_arguments' => 'strict']), 'POST')
|
||||||
->write('/generator/pin', new route('generator', 'pin'), 'GET')
|
->write('/generator/pin', new route('generator', 'pin'), 'GET')
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -10,17 +10,25 @@
|
|||||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||||
*/
|
*/
|
||||||
class core {
|
class core {
|
||||||
|
/**
|
||||||
|
* @name Global modules
|
||||||
|
*
|
||||||
|
* @type {object}
|
||||||
|
*/
|
||||||
|
static global = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name System modules
|
||||||
|
*
|
||||||
|
* @type {object}
|
||||||
|
*/
|
||||||
|
static system = {};
|
||||||
|
|
||||||
// Domain
|
// Domain
|
||||||
static domain = window.location.hostname;
|
static domain = window.location.hostname;
|
||||||
|
|
||||||
// Language
|
// Language
|
||||||
static language = "english";
|
static language = "ru";
|
||||||
|
|
||||||
// Currency
|
|
||||||
static currency = { name: "usd", symbol: "$" };
|
|
||||||
|
|
||||||
// Theme
|
|
||||||
static theme = window.getComputedStyle(document.getElementById("theme"));
|
|
||||||
|
|
||||||
// Window
|
// Window
|
||||||
static window;
|
static window;
|
||||||
@@ -81,6 +89,42 @@ class core {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Buffer
|
||||||
|
*/
|
||||||
|
static buffer = class buffer {
|
||||||
|
/**
|
||||||
|
* @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 {bool} Execution completed with an error?
|
||||||
|
*/
|
||||||
|
static write(name, value) {
|
||||||
|
core.modules.connect("damper").then(
|
||||||
|
() => {
|
||||||
|
// Imported the damper module
|
||||||
|
|
||||||
|
// Execute under damper
|
||||||
|
this.write.damper(name, value);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// Not imported the damper module
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
this.write.system(name, value);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сгенерировать окно выбора действия
|
* Сгенерировать окно выбора действия
|
||||||
*
|
*
|
||||||
@@ -95,7 +139,7 @@ class core {
|
|||||||
*
|
*
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
/* static choose = core.damper(
|
/* static choose = core.global.damper(
|
||||||
(
|
(
|
||||||
title = "Выбор действия",
|
title = "Выбор действия",
|
||||||
text = "",
|
text = "",
|
||||||
@@ -269,32 +313,125 @@ Object.assign(
|
|||||||
/**
|
/**
|
||||||
* @name Connect modules
|
* @name Connect modules
|
||||||
*
|
*
|
||||||
* @param {Array|string} modules Names of modules or name of the module
|
* @param {(Array|string)} global Names of global modules without extension (`.mjs` only)
|
||||||
|
* @param {(Array|string)} system Names of system modules without extenstion (`.mjs` only)
|
||||||
*
|
*
|
||||||
* @return {Prommise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async connect(modules) {
|
async connect(global, system) {
|
||||||
// Normalisation required argiments
|
// Normalisation required arguments
|
||||||
if (typeof modules === "string") modules = [modules];
|
if (typeof global === "string") global = [global];
|
||||||
|
if (typeof system === "string") system = [system];
|
||||||
|
|
||||||
if (modules instanceof Array) {
|
// Initializing the registry of connected modules
|
||||||
// Received and validated required arguments
|
const connected = {
|
||||||
|
system: [],
|
||||||
|
global: []
|
||||||
|
};
|
||||||
|
|
||||||
// Initializing the registry of loaded modules
|
if (global?.length > 0) {
|
||||||
const loaded = [];
|
// Global
|
||||||
|
|
||||||
for (const module of modules) {
|
for (const module of global) {
|
||||||
// Iterating over modules
|
// Iterating over global modules
|
||||||
|
|
||||||
// Downloading, importing and writing the module into a core property and into registry of loaded modules
|
// Downloading, importing and writing the global module into a core property and into registry of connected modules
|
||||||
core[module] =
|
core.global[module] =
|
||||||
loaded[module] =
|
connected.global[module] =
|
||||||
await (await import(`./modules/${module}.mjs`)).default;
|
await (await import(`./modules/${module}.mjs`)).default;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (system?.length > 0) {
|
||||||
|
// System
|
||||||
|
|
||||||
|
for (const module of system) {
|
||||||
|
// Iterating over system modules
|
||||||
|
|
||||||
|
// Downloading, importing and writing the system module into a core property and into registry of connected modules
|
||||||
|
core.system[module] =
|
||||||
|
connected.system[module] =
|
||||||
|
await (await import(`./modules/system/${module}.mjs`)).default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit (success)
|
||||||
|
return connected;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
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.global.damper(
|
||||||
|
(...variables) => core.buffer.write.system(...variables),
|
||||||
|
300,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
||||||
|
core.session.buffer?.write(name, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
core.modules.connect("account").then(() => {
|
||||||
|
// Imported the account module
|
||||||
|
|
||||||
|
// Write to the account buffer
|
||||||
|
core.account.buffer?.write(name, value);
|
||||||
|
});
|
||||||
|
|
||||||
// Exit (success)
|
// Exit (success)
|
||||||
return loaded;
|
resolve();
|
||||||
|
} catch (e) {
|
||||||
|
// Exit (fail)
|
||||||
|
reject(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Dispatching event: "core.initialized"
|
||||||
|
document.dispatchEvent(new CustomEvent("core.initialized"));
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
../../../../../../damper.mjs/damper.min.mjs
|
../../../../../../damper.mjs/damper.mjs
|
||||||
@@ -66,7 +66,7 @@ core.modules.connect("damper").then(() => {
|
|||||||
*
|
*
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
damper: core.damper(
|
damper: core.global.damper(
|
||||||
(...variables) => generator.print.system(...variables),
|
(...variables) => generator.print.system(...variables),
|
||||||
300,
|
300,
|
||||||
4,
|
4,
|
||||||
@@ -117,10 +117,25 @@ Object.assign(
|
|||||||
|
|
||||||
// Writing parameters into the body buffer
|
// Writing parameters into the body buffer
|
||||||
body.append("type", type);
|
body.append("type", type);
|
||||||
body.append("amount", amount);
|
|
||||||
|
|
||||||
// Initializing the display JSON buffer
|
// Initializing the canvas JSON buffer
|
||||||
const display = {};
|
/* const canvas = {
|
||||||
|
width:
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
// const background = ;
|
||||||
|
|
||||||
|
// Writing canvas into the body buffer
|
||||||
|
body.append("canvas", JSON.stringify({
|
||||||
|
width: canvas.clientWidth,
|
||||||
|
height: canvas.clientHeight
|
||||||
|
}));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Iterating over products
|
||||||
|
|
||||||
|
// Initializing the product JSON buffer
|
||||||
|
const product = {};
|
||||||
|
|
||||||
for (const layer of canvas.querySelectorAll("div.layer")) {
|
for (const layer of canvas.querySelectorAll("div.layer")) {
|
||||||
// Iterating over layers
|
// Iterating over layers
|
||||||
@@ -134,15 +149,14 @@ Object.assign(
|
|||||||
// Initialized the layer index
|
// Initialized the layer index
|
||||||
|
|
||||||
// Initializing the layer
|
// Initializing the layer
|
||||||
display[index] ??= {};
|
product[index] ??= {};
|
||||||
|
|
||||||
// Writing the layer parameters
|
// Writing the layer parameters
|
||||||
display[index].x =
|
product[index].x =
|
||||||
parseInt(layer.style.getPropertyValue("left")) || 0;
|
parseInt(layer.style.getPropertyValue("left")) || 0;
|
||||||
display[index].y =
|
product[index].y =
|
||||||
parseInt(layer.style.getPropertyValue("top")) || 0;
|
parseInt(layer.style.getPropertyValue("top")) || 0;
|
||||||
display[index].scale =
|
product[index].scale = parseFloat(layer.style.getPropertyValue("scale") || 1);
|
||||||
(parseInt(layer.style.getPropertyValue("scale")) || 1) * 100;
|
|
||||||
|
|
||||||
// Initializing the layer type
|
// Initializing the layer type
|
||||||
const type = layer.getAttribute("data-layer-type");
|
const type = layer.getAttribute("data-layer-type");
|
||||||
@@ -168,14 +182,14 @@ Object.assign(
|
|||||||
await fetch(src).then((r) => r.blob()).then((value) => {
|
await fetch(src).then((r) => r.blob()).then((value) => {
|
||||||
// Converted "blob:..." string to the Blob object
|
// Converted "blob:..." string to the Blob object
|
||||||
|
|
||||||
// Writing into the body buffer
|
// Writing into the body buffer (`{$product}_{$layer}`)
|
||||||
body.append("layer_" + index + "_image", value, index);
|
body.append("0_" + index, value, index);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// URL (expected)
|
// URL (expected)
|
||||||
|
|
||||||
// Writing the layer parameter
|
// Writing the layer parameter
|
||||||
display[index].image = src;
|
product[index].image = src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,8 +199,8 @@ Object.assign(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writing into the body buffer
|
// Writing products into the body buffer (the only one canvas)
|
||||||
body.append("display", JSON.stringify(display));
|
body.append("products", JSON.stringify({0: product}));
|
||||||
|
|
||||||
return await core.request(
|
return await core.request(
|
||||||
"/generator/print",
|
"/generator/print",
|
||||||
@@ -226,6 +240,3 @@ Object.assign(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Connecting to the core
|
|
||||||
if (!core.generator) core.generator = generator;
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
../../../../../../pechatalka.mjs/pechatalka.min.mjs
|
../../../../../../pechatalka.mjs/pechatalka.mjs
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
core.modules.connect(["pechatalka"])
|
core.modules.connect(["generator", "pechatalka"])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Imported the pechatalka module
|
// Imported modules
|
||||||
|
|
||||||
// Initializing the instance
|
// Initializing the instance
|
||||||
const instance = new core.pechatalka(
|
const instance = core.global.pechatalka = new core.global.pechatalka(
|
||||||
document.getElementById("pechatalka"),
|
document.getElementById("pechatalka"),
|
||||||
document.getElementById("pechatalka")?.querySelector(".canvas"),
|
document.getElementById("pechatalka")?.querySelector(".canvas"),
|
||||||
document.getElementById("pechatalka")?.querySelector(".result"),
|
document.getElementById("pechatalka")?.querySelector(".result"),
|
||||||
@@ -11,12 +11,8 @@ core.modules.connect(["pechatalka"])
|
|||||||
["interface", true],
|
["interface", true],
|
||||||
["round", true],
|
["round", true],
|
||||||
]),
|
]),
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reinitializing the core instance of pechatalka
|
|
||||||
core.pechatalka = instance;
|
|
||||||
|
|
||||||
// instence.layers.set('interface', true)
|
// instence.layers.set('interface', true)
|
||||||
|
|
||||||
// Reinitializing the `layer create` event
|
// Reinitializing the `layer create` event
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@charset "UTF-8";
|
@charset "UTF-8";
|
||||||
|
|
||||||
section#title {
|
div#title {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -23,7 +23,7 @@ section#title {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section#pechatalka {
|
div#pechatalka {
|
||||||
--width: 300px;
|
--width: 300px;
|
||||||
width: var(--width, 300px);
|
width: var(--width, 300px);
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -33,7 +33,7 @@ section#pechatalka {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
|
|
||||||
>section.system {
|
>div.system {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ section#pechatalka {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>section.canvas.pin {
|
>div.canvas.pin {
|
||||||
--diameter-cut: 250px;
|
--diameter-cut: 250px;
|
||||||
--offset: calc(37 / 48 * 100);
|
--offset: calc(37 / 48 * 100);
|
||||||
--diameter-display: calc(var(--diameter-cut) * var(--offset) / 100);
|
--diameter-display: calc(var(--diameter-cut) * var(--offset) / 100);
|
||||||
@@ -150,10 +150,12 @@ section#pechatalka {
|
|||||||
>div.layer {
|
>div.layer {
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
min-width: 50%;
|
/* min-width: 50%;
|
||||||
max-width: 200%;
|
max-width: 200%; */
|
||||||
width: calc(var(--diameter-cut, var(--width, 100%)) + var(--width-zoom, 0px));
|
width: calc(var(--diameter-cut, var(--width, 100%)) + var(--width-zoom, 0px));
|
||||||
|
height: calc(var(--diameter-cut, var(--width, 100%)) + var(--width-zoom, 0px));
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
|
align-content: center;
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
@@ -195,8 +197,8 @@ section#pechatalka {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:has(> section.canvas.pin > div.layer) {
|
&:has(> div.canvas.pin > div.layer) {
|
||||||
>section.result {
|
>div.result {
|
||||||
>button.buy {
|
>button.buy {
|
||||||
cursor: pointer !important;
|
cursor: pointer !important;
|
||||||
filter: unset;
|
filter: unset;
|
||||||
@@ -204,7 +206,7 @@ section#pechatalka {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
>section.result {
|
>div.result {
|
||||||
/* margin-top: auto; */
|
/* margin-top: auto; */
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
2
svoboda/pechatalka/system/storage/products/.gitignore
vendored
Normal file
2
svoboda/pechatalka/system/storage/products/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
@@ -1,29 +1,29 @@
|
|||||||
<section id="pechatalka">
|
<div id="pechatalka">
|
||||||
<section class="system">
|
<div class="system">
|
||||||
<input id="pechatalka_add_image" type="file" name="images" accept="image/png, image/jpeg, image/webp"
|
<input id="pechatalka_add_image" type="file" name="images" accept="image/png, image/jpeg, image/webp"
|
||||||
multiple="false" onchange="document.getElementById('pechatalka')?.pechatalka.image(this.files[0], 160);" />
|
multiple="false" onchange="core.global.pechatalka.image(this.files[0], 160);" />
|
||||||
<input id="pechatalka_background" type="color" name="background" value="#ffffff"
|
<input id="pechatalka_background" type="color" name="background" value="#ffffff"
|
||||||
oninput="core.pechatalka?.wrap?.querySelector('label[for=\'pechatalka_background\']>div.color')?.style.setProperty('--color', event.target.value); core.pechatalka.canvas.style.setProperty('background-color', event.target.value);" />
|
oninput="core.global.pechatalka?.wrap?.querySelector('label[for=\'pechatalka_background\']>div.color')?.style.setProperty('--color', event.target.value); core.global.pechatalka.canvas.style.setProperty('background-color', event.target.value);" />
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<nav class="tools rounded unselectable">
|
<nav class="tools rounded unselectable">
|
||||||
<label class="button" for="pechatalka_add_image"><i class="icon plus"></i></label>
|
<label class="button" for="pechatalka_add_image"><i class="icon plus"></i></label>
|
||||||
<button id="pechatalka_interface" onclick="const icon = this.getElementsByTagName('i')[0]; icon.classList.toggle('disabled'); core.pechatalka.global('interface', !icon.classList.contains('disabled'), true);"><i class="icon notification"></i></button>
|
<button id="pechatalka_interface" onclick="const icon = this.getElementsByTagName('i')[0]; icon.classList.toggle('disabled'); core.global.pechatalka.global('interface', !icon.classList.contains('disabled'), true);"><i class="icon notification"></i></button>
|
||||||
<button id="pechatalka_round" onclick="const icon = this.getElementsByTagName('i')[0]; icon.classList.toggle('disabled'); core.pechatalka.global('round', !icon.classList.contains('disabled'), true);"><i class="icon round"></i></button>
|
<button id="pechatalka_round" onclick="const icon = this.getElementsByTagName('i')[0]; icon.classList.toggle('disabled'); core.global.pechatalka.global('round', !icon.classList.contains('disabled'), true);"><i class="icon round"></i></button>
|
||||||
<label class="button" for="pechatalka_background"><div class="color"></div></label>
|
<label class="button" for="pechatalka_background"><div class="color"></div></label>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<section class="canvas pin unselectable">
|
<div class="canvas pin unselectable">
|
||||||
<label class="button add" for="pechatalka_add_image"><i class="icon plus"></i></label>
|
<label class="button add" for="pechatalka_add_image"><i class="icon plus"></i></label>
|
||||||
|
|
||||||
<div class="display"></div>
|
<div class="display"></div>
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
<section class="result unselectable">
|
<div class="result unselectable">
|
||||||
<button class="print rounded"
|
<button class="print rounded"
|
||||||
onclick="core.generator?.print('pin_37', 1, document.getElementById('pechatalka')?.pechatalka.canvas, this)"><i
|
onclick="core.global.generator?.print('pin_37', 1, core.global.pechatalka.canvas, this)"><i
|
||||||
class="icon printer"></i></button>
|
class="icon printer"></i></button>
|
||||||
<span class="cost" style="--currency: '{{ currency.symbol() ?? '?'}}'">0</span>
|
<span class="cost" style="--currency: '{{ currency.symbol() ?? '?'}}'">0</span>
|
||||||
<button class="buy rounded"></button>
|
<button class="buy rounded"></button>
|
||||||
</section>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<section id="title" class="unselectable">
|
<div id="title" class="unselectable">
|
||||||
<h1>{{ language.name == 'ru' ? 'Печаталка' : 'Pechatalka' }}</h1>
|
<h1>{{ language.name == 'ru' ? 'Печаталка' : 'Pechatalka' }}</h1>
|
||||||
<small>{{ language.name == 'ru' ? 'Рабочий Союз Свободы' : 'Svoboda Work Union' }}</small>
|
<small>{{ language.name == 'ru' ? 'Рабочий Союз Свободы' : 'Svoboda Work Union' }}</small>
|
||||||
</section>
|
</div>
|
||||||
|
|||||||
@@ -1,26 +1,10 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
{% for element in js %}
|
|
||||||
<script {% if element.src %}src="{{ element.src }}"{% endif %} {% if element.type %}type="{{ element.type }}"{% endif %}>{{ element.innerText }}</script>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||||
<script src="/js/modules/damper.mjs" type="module"></script>
|
<script src="/js/modules/damper.mjs" type="module"></script>
|
||||||
<script src="/js/core.js"></script>
|
<script src="/js/core.js"></script>
|
||||||
<script src="/js/modules/telegram.mjs" type="module"></script>
|
<script src="/js/modules/telegram.mjs" type="module"></script>
|
||||||
<script src="/js/telegram.js"></script>
|
<script src="/js/telegram.js"></script>
|
||||||
|
|
||||||
{% if javascript is not empty %}
|
|
||||||
<script>
|
|
||||||
core.modules.connect("damper").then(() => {
|
|
||||||
let _window;
|
|
||||||
|
|
||||||
{% for code in javascript %}
|
|
||||||
{{ code|raw }}
|
|
||||||
{% endfor %}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import("/js/core.js").then(() => {
|
import("/js/core.js").then(() => {
|
||||||
core.language = '{{ language.value }}';
|
core.language = '{{ language.value }}';
|
||||||
|
|||||||
Reference in New Issue
Block a user