first commit
No files matched your search
@@ -0,0 +1 @@
|
||||
config.json
|
||||
@@ -0,0 +1,23 @@
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
|
||||
ExpiresByType image/avif "access plus 1 year"
|
||||
ExpiresByType image/webp "access plus 1 year"
|
||||
ExpiresByType image/jpeg "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/svg+xml "access plus 1 year"
|
||||
ExpiresByType image/svg "access plus 1 year"
|
||||
ExpiresByType image/x-icon "access plus 1 year"
|
||||
|
||||
ExpiresByType font/woff2 "access plus 1 year"
|
||||
ExpiresByType font/woff "access plus 1 year"
|
||||
|
||||
ExpiresByType text/css "access plus 1 month"
|
||||
ExpiresByType application/javascript "access plus 1 month"
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
<FilesMatch "\.(avif|webp|jpe?g|png|gif|ico|svg|woff2?|css|js)$">
|
||||
Header set Cache-Control "max-age=31536000, public, immutable"
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
@@ -0,0 +1,68 @@
|
||||
/* Box sizing */
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
|
||||
/* Grid & Containers */
|
||||
.container { width: 100%; padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; }
|
||||
@media (min-width: 576px) { .container { max-width: 540px; } }
|
||||
@media (min-width: 768px) { .container { max-width: 720px; } }
|
||||
@media (min-width: 992px) { .container { max-width: 960px; } }
|
||||
@media (min-width: 1200px) { .container { max-width: 1140px; } }
|
||||
|
||||
.row { display: flex; flex-wrap: wrap; margin-right: -15px; margin-left: -15px; }
|
||||
.col-6, .col-md-4, .col-md-5, .col-md-7 { position: relative; width: 100%; padding-right: 15px; padding-left: 15px; }
|
||||
.col-6 { flex: 0 0 50%; max-width: 50%; }
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.col-md-4 { flex: 0 0 33.333333%; max-width: 33.333333%; }
|
||||
.col-md-5 { flex: 0 0 41.666667%; max-width: 41.666667%; }
|
||||
.col-md-7 { flex: 0 0 58.333333%; max-width: 58.333333%; }
|
||||
.order-md-1 { order: 1; }
|
||||
.d-md-none { display: none !important; }
|
||||
.d-md-flex { display: flex !important; }
|
||||
.py-md-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
|
||||
.mb-md-0 { margin-bottom: 0 !important; }
|
||||
.text-md-right { text-align: right !important; }
|
||||
}
|
||||
|
||||
/* Flex & Display */
|
||||
.d-none { display: none !important; }
|
||||
.d-flex { display: flex !important; }
|
||||
.flex-column { flex-direction: column !important; }
|
||||
.align-items-center { align-items: center !important; }
|
||||
|
||||
/* Spacing */
|
||||
.m-0 { margin: 0 !important; }
|
||||
.mb-0 { margin-bottom: 0 !important; }
|
||||
.mb-2 { margin-bottom: 0.5rem !important; }
|
||||
.mb-3 { margin-bottom: 1rem !important; }
|
||||
.mb-4 { margin-bottom: 1.5rem !important; }
|
||||
.mt-4 { margin-top: 1.5rem !important; }
|
||||
.mr-4 { margin-right: 1.5rem !important; }
|
||||
.px-0 { padding-left: 0 !important; padding-right: 0 !important; }
|
||||
.py-1 { padding-top: 0.25rem !important; padding-bottom: 0.25rem !important; }
|
||||
.py-3 { padding-top: 1rem !important; padding-bottom: 1rem !important; }
|
||||
.py-4 { padding-top: 1.5rem !important; padding-bottom: 1.5rem !important; }
|
||||
.py-5 { padding-top: 3rem !important; padding-bottom: 3rem !important; }
|
||||
|
||||
/* Typography & Lists */
|
||||
.h5 { font-size: 1.25rem; font-weight: 500; line-height: 1.2; margin-bottom: 0.5rem; }
|
||||
.list-unstyled { padding-left: 0; list-style: none; }
|
||||
|
||||
/* Navbar & Collapse */
|
||||
.navbar { position: relative; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; }
|
||||
.navbar-brand { display: inline-block; padding-top: 0.3125rem; padding-bottom: 0.3125rem; white-space: nowrap; }
|
||||
.navbar-toggler { padding: 0.25rem 0.75rem; font-size: 1.25rem; line-height: 1; background: transparent; border: 1px solid rgba(255,255,255,0.1); border-radius: 0.25rem; cursor: pointer; }
|
||||
.navbar-toggler-icon { display: inline-block; width: 1.5em; height: 1.5em; vertical-align: middle; content: ""; background: no-repeat center center; background-size: 100% 100%; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.7%29' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); }
|
||||
.navbar-collapse { flex-basis: 100%; flex-grow: 1; align-items: center; }
|
||||
.collapse:not(.show) { display: none; }
|
||||
|
||||
/* Nav Links & Pills */
|
||||
.nav { display: flex; flex-wrap: wrap; padding-left: 0; margin-bottom: 0; list-style: none; }
|
||||
.nav-link { display: block; padding: 0.5rem 1rem; text-decoration: none; }
|
||||
|
||||
/* Buttons */
|
||||
.btn { display: inline-block; font-weight: 400; text-align: center; vertical-align: middle; user-select: none; border: 1px solid transparent; padding: 0.375rem 0.75rem; font-size: 1rem; line-height: 1.5; border-radius: 0.25rem; text-decoration: none; cursor: pointer; }
|
||||
|
||||
/* Progress bar */
|
||||
.progress { display: flex; height: 1rem; overflow: hidden; font-size: 0.75rem; background-color: #e9ecef; border-radius: 0.25rem; }
|
||||
.progress-bar { display: flex; flex-direction: column; justify-content: center; overflow: hidden; color: #fff; text-align: center; white-space: nowrap; background-color: #007bff; transition: width 0.6s ease; }
|
||||
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* Bootstrap v4.6.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2022 The Bootstrap Authors
|
||||
* Copyright 2011-2022 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:.875em;font-weight:400}.mark,mark{padding:.2em;background-colorLine truncated
|
||||
/*# sourceMappingURL=bootstrap.min.css.map */
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
$demoBasePath = rtrim($assetsUriPrefix, '/') . "/images/demo/{$indpic}";
|
||||
$demoLocalPath = __DIR__ . "/../images/demo/{$indpic}";
|
||||
|
||||
$getAssetUrl = function (string $relativePath) use ($demoBasePath, $demoLocalPath): string {
|
||||
$fullPath = $demoLocalPath . '/' . ltrim($relativePath, '/');
|
||||
$version = file_exists($fullPath) ? filemtime($fullPath) : '1';
|
||||
return $demoBasePath . '/' . ltrim($relativePath, '/') . '?v=' . $version;
|
||||
};
|
||||
|
||||
$labelBefore = Yii::t('app', 'До');
|
||||
$labelAfter = Yii::t('app', 'После');
|
||||
|
||||
$sourceMob = $getAssetUrl('1/source_mob.avif');
|
||||
$sourceOrig = $getAssetUrl('1/source.avif');
|
||||
|
||||
$resultMob = $getAssetUrl('1/result_mob.avif');
|
||||
$resultOrig = $getAssetUrl('1/result.avif');
|
||||
|
||||
?>
|
||||
|
||||
<div id="examples">
|
||||
|
||||
<div class="image-compare" id="image-compare">
|
||||
<img src="<?= $sourceMob ?>"
|
||||
srcset="<?= $sourceMob ?> 768w, <?= $sourceOrig ?> 1696w"
|
||||
sizes="(max-width: 767.98px) 100vw, 728px"
|
||||
id="image-compare-source"
|
||||
fetchpriority="high"
|
||||
loading="eager"
|
||||
width="728"
|
||||
height="395"
|
||||
alt="<?= htmlspecialchars($labelBefore) ?>">
|
||||
|
||||
<img src="<?= $resultMob ?>"
|
||||
srcset="<?= $resultMob ?> 768w, <?= $resultOrig ?> 1696w"
|
||||
sizes="(max-width: 767.98px) 100vw, 728px"
|
||||
id="image-compare-result"
|
||||
fetchpriority="high"
|
||||
loading="eager"
|
||||
width="728"
|
||||
height="395"
|
||||
alt="<?= htmlspecialchars($labelAfter) ?>">
|
||||
</div>
|
||||
|
||||
<div class="image-examples">
|
||||
<div class="image-examples-label"><?= Yii::t('app', 'Примеры изображений') ?></div>
|
||||
<div class="image-examples-list">
|
||||
<?php for ($i = 1; $i <= 5; $i++):
|
||||
$thumbMob = $getAssetUrl("{$i}/thumb_mob.avif");
|
||||
$thumbOrig = $getAssetUrl("{$i}/thumb.avif");
|
||||
?>
|
||||
<div class="image-examples-item <?= $i === 1 ? 'active' : '' ?>"
|
||||
data-index="<?= $i ?>"
|
||||
data-source="<?= $getAssetUrl("{$i}/source.avif") ?>"
|
||||
data-result="<?= $getAssetUrl("{$i}/result.avif") ?>"
|
||||
data-source-mob="<?= $getAssetUrl("{$i}/source_mob.avif") ?>"
|
||||
data-result-mob="<?= $getAssetUrl("{$i}/result_mob.avif") ?>">
|
||||
<div class="image-examples-image">
|
||||
<?php if ($i === 1): ?>
|
||||
<picture>
|
||||
<source media="(max-width: 767.98px)" srcset="<?= $thumbMob ?>">
|
||||
<img src="<?= $thumbOrig ?>" width="53" height="53" alt="Example 1">
|
||||
</picture>
|
||||
<?php else: ?>
|
||||
<picture class="deferred-picture">
|
||||
<source media="(max-width: 767.98px)" data-srcset="<?= $thumbMob ?>">
|
||||
<img data-src="<?= $thumbOrig ?>" width="53" height="53" alt="Example <?= $i ?>" class="deferred-thumb">
|
||||
</picture>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const examples = {
|
||||
el: document.getElementById('examples'),
|
||||
titleEl: document.getElementById('form-title'),
|
||||
|
||||
hide() {
|
||||
window.scrollTo(0, 0);
|
||||
window.scrollTo({ top: 0, behavior: 'instant' });
|
||||
if (this.el) this.el.style.display = 'none';
|
||||
},
|
||||
show() {
|
||||
if (this.el) this.el.style.display = '';
|
||||
},
|
||||
setTitle(html) {
|
||||
if (this.titleEl) this.titleEl.innerHTML = html;
|
||||
}
|
||||
};
|
||||
|
||||
function setupNativeCompare(element, labelBefore, labelAfter) {
|
||||
if (!element || element.classList.contains('native-icv') || element.classList.contains('icv')) return null;
|
||||
|
||||
const sourceImg = element.querySelector('#image-compare-source');
|
||||
const resultImg = element.querySelector('#image-compare-result');
|
||||
if (!sourceImg || !resultImg) return null;
|
||||
|
||||
element.classList.add('native-icv');
|
||||
|
||||
const beforeWrap = document.createElement('div');
|
||||
beforeWrap.className = 'icv__before-wrap';
|
||||
|
||||
resultImg.className = 'icv__after-img';
|
||||
|
||||
const handle = document.createElement('div');
|
||||
handle.className = 'icv__handle';
|
||||
const circle = document.createElement('div');
|
||||
circle.className = 'icv__handle-circle';
|
||||
handle.appendChild(circle);
|
||||
|
||||
const lblBefore = document.createElement('div');
|
||||
lblBefore.className = 'icv__label icv__label-before';
|
||||
lblBefore.textContent = labelBefore;
|
||||
|
||||
const lblAfter = document.createElement('div');
|
||||
lblAfter.className = 'icv__label icv__label-after';
|
||||
lblAfter.textContent = labelAfter;
|
||||
|
||||
beforeWrap.appendChild(sourceImg);
|
||||
|
||||
element.appendChild(resultImg);
|
||||
element.appendChild(beforeWrap);
|
||||
element.appendChild(handle);
|
||||
element.appendChild(lblBefore);
|
||||
element.appendChild(lblAfter);
|
||||
|
||||
function updateSlider(percent) {
|
||||
percent = Math.max(0, Math.min(100, percent));
|
||||
beforeWrap.style.width = percent + '%';
|
||||
handle.style.left = percent + '%';
|
||||
sourceImg.style.width = element.clientWidth + 'px';
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
sourceImg.style.width = element.clientWidth + 'px';
|
||||
});
|
||||
|
||||
let isDragging = false;
|
||||
function getPos(e) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
||||
return ((clientX - rect.left) / rect.width) * 100;
|
||||
}
|
||||
|
||||
function onStart(e) {
|
||||
isDragging = true;
|
||||
updateSlider(getPos(e));
|
||||
}
|
||||
|
||||
function onMove(e) {
|
||||
if (!isDragging) return;
|
||||
updateSlider(getPos(e));
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
element.addEventListener('mousedown', onStart);
|
||||
element.addEventListener('touchstart', onStart, { passive: true });
|
||||
|
||||
window.addEventListener('mousemove', onMove);
|
||||
window.addEventListener('touchmove', onMove, { passive: true });
|
||||
|
||||
window.addEventListener('mouseup', onEnd);
|
||||
window.addEventListener('touchend', onEnd);
|
||||
|
||||
updateSlider(50);
|
||||
|
||||
return {
|
||||
setImages(sourceUrl, resultUrl) {
|
||||
sourceImg.removeAttribute('srcset');
|
||||
resultImg.removeAttribute('srcset');
|
||||
sourceImg.src = sourceUrl;
|
||||
resultImg.src = resultUrl;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function initCompareViewer() {
|
||||
const element = document.getElementById('image-compare');
|
||||
const beforeTxt = '<?= addslashes($labelBefore) ?>';
|
||||
const afterTxt = '<?= addslashes($labelAfter) ?>';
|
||||
|
||||
if (element && typeof ImageCompare !== 'undefined') {
|
||||
const options = {
|
||||
controlColor: "#FFFFFF",
|
||||
controlShadow: true,
|
||||
addCircle: true,
|
||||
addCircleBlur: true,
|
||||
showLabels: true,
|
||||
labelOptions: { before: beforeTxt, after: afterTxt, onHover: false },
|
||||
smoothing: true,
|
||||
smoothingAmount: 100,
|
||||
hoverStart: false,
|
||||
verticalMode: false,
|
||||
startingPoint: 50,
|
||||
fluidMode: false
|
||||
};
|
||||
window.icvInstance = new ImageCompare(element, options).mount();
|
||||
} else if (element) {
|
||||
window.nativeCompareInstance = setupNativeCompare(element, beforeTxt, afterTxt);
|
||||
}
|
||||
|
||||
const exampleItems = document.querySelectorAll('.image-examples-item');
|
||||
exampleItems.forEach(item => {
|
||||
item.onclick = function() {
|
||||
const isMobile = window.innerWidth < 768;
|
||||
// На мобильном берем source_mob, на ПК — полный оригинал
|
||||
const source = (isMobile && this.getAttribute('data-source-mob'))
|
||||
? this.getAttribute('data-source-mob')
|
||||
: this.getAttribute('data-source');
|
||||
const result = (isMobile && this.getAttribute('data-result-mob'))
|
||||
? this.getAttribute('data-result-mob')
|
||||
: this.getAttribute('data-result');
|
||||
|
||||
if (window.icvInstance) {
|
||||
window.icvInstance.images.start.element.removeAttribute('srcset');
|
||||
window.icvInstance.images.end.element.removeAttribute('srcset');
|
||||
window.icvInstance.images.start.element.src = source;
|
||||
window.icvInstance.images.end.element.src = result;
|
||||
} else if (window.nativeCompareInstance) {
|
||||
window.nativeCompareInstance.setImages(source, result);
|
||||
} else {
|
||||
const sourceEl = document.getElementById('image-compare-source');
|
||||
const resultEl = document.getElementById('image-compare-result');
|
||||
if (sourceEl) {
|
||||
sourceEl.removeAttribute('srcset');
|
||||
sourceEl.src = source;
|
||||
}
|
||||
if (resultEl) {
|
||||
resultEl.removeAttribute('srcset');
|
||||
resultEl.src = result;
|
||||
}
|
||||
}
|
||||
|
||||
exampleItems.forEach(el => el.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function initStrictImagePipeline() {
|
||||
initCompareViewer();
|
||||
|
||||
const preloadUrl = (url) => {
|
||||
return new Promise(resolve => {
|
||||
const img = new Image();
|
||||
img.onload = img.onerror = resolve;
|
||||
img.src = url;
|
||||
});
|
||||
};
|
||||
|
||||
const deferredPics = document.querySelectorAll('.deferred-picture');
|
||||
const thumbPromises = [];
|
||||
|
||||
deferredPics.forEach(pic => {
|
||||
const source = pic.querySelector('source');
|
||||
const img = pic.querySelector('img');
|
||||
|
||||
if (source && source.getAttribute('data-srcset')) {
|
||||
source.srcset = source.getAttribute('data-srcset');
|
||||
}
|
||||
if (img && img.getAttribute('data-src')) {
|
||||
const p = new Promise(resolve => {
|
||||
img.onload = img.onerror = resolve;
|
||||
img.src = img.getAttribute('data-src');
|
||||
});
|
||||
thumbPromises.push(p);
|
||||
}
|
||||
});
|
||||
|
||||
Promise.all(thumbPromises).then(() => {
|
||||
const queue = [2, 3, 4, 5];
|
||||
|
||||
async function loadSequentially() {
|
||||
const isMobile = window.innerWidth < 768;
|
||||
for (const num of queue) {
|
||||
const item = document.querySelector(`.image-examples-item[data-index="${num}"]`);
|
||||
if (!item) continue;
|
||||
|
||||
const s = (isMobile && item.getAttribute('data-source-mob'))
|
||||
? item.getAttribute('data-source-mob')
|
||||
: item.getAttribute('data-source');
|
||||
const r = (isMobile && item.getAttribute('data-result-mob'))
|
||||
? item.getAttribute('data-result-mob')
|
||||
: item.getAttribute('data-result');
|
||||
|
||||
await Promise.all([preloadUrl(s), preloadUrl(r)]);
|
||||
}
|
||||
}
|
||||
|
||||
if ('requestIdleCallback' in window) {
|
||||
requestIdleCallback(() => loadSequentially());
|
||||
} else {
|
||||
setTimeout(loadSequentially, 150);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initStrictImagePipeline);
|
||||
} else {
|
||||
initStrictImagePipeline();
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
$maxSide = 6000;
|
||||
|
||||
$errorSizeMessage = Yii::t('app', 'Изображение слишком большое ({width}x{height}px). Максимальный размер — {maxSide}px по большей стороне.', [
|
||||
'maxSide' => $maxSide,
|
||||
]);
|
||||
$errorMessage = Yii::t('app', 'Возникла ошибка');
|
||||
$utmJson = json_encode($UTM ?? [], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$createApiUrl = ($apiHost ?? '') . '/api/images/public/create';
|
||||
$redirectUrlBase = $redirect_url_base ?? '';
|
||||
$redirectUrlUtm = $redirect_url_utm ?? '';
|
||||
|
||||
?>
|
||||
|
||||
<form action="<?= htmlspecialchars($createApiUrl, ENT_QUOTES, 'UTF-8') ?>" class="dropzone-form gradient-dashed-border" id="dropzone-form" method="POST" style="background: transparent; border: unset; display: <?= ($show ?? true) ? '' : 'none'; ?>; cursor: pointer;">
|
||||
|
||||
<div class="dropzone-form-inner">
|
||||
<div class="dropzone-form-icon">
|
||||
<img src="<?= $assetsUriPrefix; ?>/images/dropzone-icon.svg" alt="" height="64" width="64">
|
||||
</div>
|
||||
<div id="form-title" class="dropzone-form-text"><?= Yii::t('app', 'Загрузите изображение') ?> <br class="d-md-none"><?= Yii::t('app', 'для улучшения') ?></div>
|
||||
<div class="dropzone-form-format">
|
||||
<div class="dropzone-form-format-label"><?= Yii::t('app', 'Поддерживаемые форматы') ?>:</div>
|
||||
<div class="dropzone-form-format-list">
|
||||
<div class="dropzone-form-format-item">png</div>
|
||||
<div class="dropzone-form-format-item">jpeg</div>
|
||||
<div class="dropzone-form-format-item">jpg</div>
|
||||
<div class="dropzone-form-format-item">webp</div>
|
||||
<div class="dropzone-form-format-item">heic</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropzone-form-action">
|
||||
<button type="button" class="dropzone-form-button">
|
||||
<span><?= Yii::t('app', 'Загрузить изображение') ?></span>
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.3333 14.6667C15.6345 14.6667 17.5 12.8012 17.5 10.5C17.5 8.43079 15.9917 6.71387 14.0145 6.38874C14.0113 6.38821 14.0086 6.38583 14.0078 6.38267C13.4802 4.43373 11.6992 3 9.58333 3C7.05203 3 5 5.05203 5 7.58333C5 7.75477 5.00941 7.924 5.02775 8.09055C5.0282 8.0947 5.0255 8.09856 5.02145 8.09957C3.57289 8.4621 2.5 9.77242 2.5 11.3333C2.5 13.1743 3.99238 14.6667 5.83333 14.6667H6.66667M10 8.83726V18M10 8.83726L7.5 11.3333M10 8.83726L12.5 11.3333" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="file" id="dropzone-file-input" name="file" accept="image/jpeg,image/png,image/jpg,image/webp,image/heic,.heic" style="display: none;">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const form = {
|
||||
el: document.getElementById('dropzone-form'),
|
||||
titleEl: document.getElementById('form-title'),
|
||||
|
||||
hide() {
|
||||
window.scrollTo(0, 0);
|
||||
window.scrollTo({ top: 0, behavior: 'instant' });
|
||||
if (this.el) this.el.style.display = 'none';
|
||||
},
|
||||
show() {
|
||||
if (this.el) this.el.style.display = '';
|
||||
},
|
||||
setTitle(html) {
|
||||
if (this.titleEl) this.titleEl.innerHTML = html;
|
||||
}
|
||||
};
|
||||
|
||||
const utmData = <?= $utmJson ?>;
|
||||
const redirectUrlBase = <?= json_encode($redirectUrlBase, JSON_UNESCAPED_UNICODE) ?>;
|
||||
const redirectUrlUtm = <?= json_encode($redirectUrlUtm, JSON_UNESCAPED_UNICODE) ?>;
|
||||
const createApiUrl = <?= json_encode($createApiUrl, JSON_UNESCAPED_UNICODE) ?>;
|
||||
|
||||
const errorSizeTpl = <?= json_encode($errorSizeMessage, JSON_UNESCAPED_UNICODE) ?>;
|
||||
const genericErrorMsg = <?= json_encode($errorMessage, JSON_UNESCAPED_UNICODE) ?>;
|
||||
|
||||
(function initNativeUploader() {
|
||||
const formEl = document.getElementById('dropzone-form');
|
||||
const fileInput = document.getElementById('dropzone-file-input');
|
||||
if (!formEl || !fileInput) return;
|
||||
|
||||
let columnsSwapped = false;
|
||||
|
||||
formEl.addEventListener('click', (e) => {
|
||||
if (e.target !== fileInput) {
|
||||
fileInput.click();
|
||||
}
|
||||
});
|
||||
|
||||
['dragenter', 'dragover'].forEach(eventName => {
|
||||
formEl.addEventListener(eventName, (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
formEl.classList.add('dragover');
|
||||
});
|
||||
});
|
||||
|
||||
['dragleave', 'dragend', 'drop'].forEach(eventName => {
|
||||
formEl.addEventListener(eventName, (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
formEl.classList.remove('dragover');
|
||||
});
|
||||
});
|
||||
|
||||
formEl.addEventListener('drop', (e) => {
|
||||
const files = e.dataTransfer?.files;
|
||||
if (files && files.length > 0) {
|
||||
handleSelectedFile(files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
fileInput.addEventListener('change', () => {
|
||||
if (fileInput.files && fileInput.files.length > 0) {
|
||||
handleSelectedFile(fileInput.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
function swapColumns() {
|
||||
if (columnsSwapped) return;
|
||||
const col5 = document.querySelector('.col-md-5');
|
||||
const col7 = document.querySelector('.col-md-7');
|
||||
if (col5 && col7) {
|
||||
const temp = document.createElement('div');
|
||||
col5.replaceWith(temp);
|
||||
col7.replaceWith(col5);
|
||||
temp.replaceWith(col7);
|
||||
columnsSwapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelectedFile(file) {
|
||||
const maxSide = 6000;
|
||||
const objectUrl = URL.createObjectURL(file);
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
const width = img.naturalWidth;
|
||||
const height = img.naturalHeight;
|
||||
|
||||
if (width > maxSide || height > maxSide) {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
fileInput.value = '';
|
||||
|
||||
const msg = errorSizeTpl
|
||||
.replace('{width}', width)
|
||||
.replace('{height}', height);
|
||||
|
||||
form.show();
|
||||
if (typeof preview !== 'undefined' && preview) {
|
||||
const col7 = document.querySelector('.col-md-7');
|
||||
if (col7) col7.appendChild(preview.el);
|
||||
preview.show();
|
||||
preview.showError(msg);
|
||||
}
|
||||
if (typeof process !== 'undefined' && process) process.hide();
|
||||
if (typeof examples !== 'undefined' && examples) examples.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
form.hide();
|
||||
swapColumns();
|
||||
|
||||
if (typeof preview !== 'undefined' && preview) {
|
||||
const col7 = document.querySelector('.col-md-7');
|
||||
if (col7) col7.appendChild(preview.el);
|
||||
preview.hide();
|
||||
preview.setSource(objectUrl);
|
||||
preview.show();
|
||||
}
|
||||
|
||||
if (typeof process !== 'undefined' && process) {
|
||||
const col5 = document.querySelector('.col-md-5');
|
||||
if (col5) col5.appendChild(process.el);
|
||||
process.setFilename(file.name);
|
||||
process.setSize(file.size);
|
||||
process.setDimensions(width, height);
|
||||
process.setStage(0);
|
||||
process.setStatus(0);
|
||||
process.show();
|
||||
}
|
||||
|
||||
if (typeof examples !== 'undefined' && examples) {
|
||||
examples.hide();
|
||||
}
|
||||
|
||||
uploadFile(file);
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
if (typeof preview !== 'undefined' && preview) {
|
||||
preview.show();
|
||||
preview.showError(genericErrorMsg);
|
||||
}
|
||||
};
|
||||
|
||||
img.src = objectUrl;
|
||||
}
|
||||
|
||||
function uploadFile(file) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const actionUrl = createApiUrl || formEl.getAttribute('action') || '/api/images/public/create';
|
||||
xhr.open('POST', actionUrl, true);
|
||||
xhr.withCredentials = true;
|
||||
xhr.setRequestHeader('Accept', 'application/json');
|
||||
|
||||
xhr.upload.onprogress = (e) => {
|
||||
if (e.lengthComputable && typeof process !== 'undefined' && process) {
|
||||
const progress = Math.round((e.loaded / e.total) * 100);
|
||||
process.setStatus(progress);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
let response;
|
||||
try {
|
||||
response = JSON.parse(xhr.responseText);
|
||||
} catch (e) {
|
||||
showUploadError(genericErrorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
const imageId = response?.id || response?.data?.id || response?.image?.id || response?.image_id;
|
||||
if (!imageId) {
|
||||
showUploadError(genericErrorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof process !== 'undefined' && process) {
|
||||
process.setStatus(100);
|
||||
process.setStage(1);
|
||||
}
|
||||
|
||||
// Build redirect target URL: $redirect_url_base . $imageId . ($redirect_url_utm ? '&' . $redirect_url_utm : '')
|
||||
let targetUrl = redirectUrlBase + encodeURIComponent(imageId);
|
||||
if (redirectUrlUtm) {
|
||||
// const sep = targetUrl.includes('?') ? '&' : '?';
|
||||
targetUrl += '&' + redirectUrlUtm;
|
||||
}
|
||||
|
||||
window.location.href = targetUrl;
|
||||
} else {
|
||||
showUploadError(genericErrorMsg);
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
showUploadError(genericErrorMsg);
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
}
|
||||
|
||||
function showUploadError(msg) {
|
||||
if (typeof process !== 'undefined' && process) process.hide();
|
||||
form.show();
|
||||
if (typeof preview !== 'undefined' && preview) preview.showError(msg);
|
||||
fileInput.value = '';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@@ -0,0 +1,99 @@
|
||||
<style>
|
||||
.image-loading {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
border-radius: 24px;
|
||||
height: 100%;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.image-loading-progress::before {
|
||||
background-image: var(--source);
|
||||
filter: blur(15px) brightness(0.7);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.image-loading-progress #source {
|
||||
/*height: 100%;*/
|
||||
z-index: 3;
|
||||
position: absolute;
|
||||
filter: brightness(0.7);
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.image-loading-error {
|
||||
display: none;
|
||||
}
|
||||
.image-loading-error.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.image-loading-progress:has(.image-loading-error.show) .image-loading-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
.image-loading-progress:has(.image-loading-error.show) #source {
|
||||
filter: brightness(0.4);
|
||||
}
|
||||
.image-loading-progress:has(.image-loading-error.show):before {
|
||||
filter: blur(15px) brightness(0.4);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.image-loading-progress {
|
||||
height: 35dvh !important;
|
||||
}
|
||||
.image-loading-progress #source {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="preview" class="image-loading image-loading-progress" style="--source: url('<?= $source ?? '' ?>'); display: <?= ($show ?? true) ? '' : 'none'; ?>" >
|
||||
<img id="source" src="<?= $source ?? '' ?>" style="height: 100%; width: 100%; object-fit: contain; flex-shrink: 0;">
|
||||
|
||||
<div class="image-loading-icon">
|
||||
<img class="sandglass" src="<?= $assetsUriPrefix ?>/images/sandglass.svg" alt="">
|
||||
<img class="spinner" src="<?= $assetsUriPrefix ?>/images/spinner.svg" alt="">
|
||||
</div>
|
||||
|
||||
<div id="upscale-error-overlay" class="image-loading-error">
|
||||
<div class="image-loading-error-icon">
|
||||
<img src="<?= $assetsUriPrefix ?>/images/confused.svg" alt="">
|
||||
</div>
|
||||
<div class="image-loading-error-title"><?= Yii::t('app', 'Извините, что-то пошло не так') ?></div>
|
||||
<div id="upscale-error-message" class="image-loading-error-text"><?= Yii::t('app', 'Максимальный допустимый размер файла 20 Мб') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const preview = {
|
||||
el: document.getElementById('preview'),
|
||||
|
||||
upscaleErrorOverlay: document.getElementById('upscale-error-overlay'),
|
||||
upscaleErrorMessage: document.getElementById('upscale-error-message'),
|
||||
sourceEl: document.getElementById('source'),
|
||||
|
||||
hide() {
|
||||
this.el.style.display = 'none';
|
||||
},
|
||||
show() {
|
||||
this.el.style.display = '';
|
||||
this.upscaleErrorOverlay.classList.remove('show');
|
||||
},
|
||||
setSource(url) {
|
||||
this.sourceEl.src = url;
|
||||
this.el.style.setProperty('--source', "url('" + url + "')");
|
||||
},
|
||||
setDimensions(w, h) {
|
||||
console.log('setSize', w, h);
|
||||
},
|
||||
setStage(stage) {
|
||||
|
||||
},
|
||||
showError(message) {
|
||||
this.upscaleErrorMessage.innerText = message || 'Ошибка обработки запроса.';
|
||||
this.upscaleErrorOverlay.classList.add('show');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
$labels = [
|
||||
'stage0' => \Yii::t('app', 'Загружаем файл на сервер'),
|
||||
'stage1' => \Yii::t('app', 'Улучшаем и увеличиваем'),
|
||||
'stage2' => \Yii::t('app', 'Загружаем полученный результат'),
|
||||
];
|
||||
$labelsJson = json_encode($labels, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
?>
|
||||
|
||||
<div id="process" class="dropzone-form dropzone-form-loading gradient-dashed-border" style="display: <?= ($show ?? true) ? '' : 'none'; ?>">
|
||||
<div class="dropzone-form-inner">
|
||||
<div id="process-title" class="dropzone-form-text"><?= \Yii::t('app', 'Загружаем файл на сервер') ?></div>
|
||||
<div id="process-filename" class="dropzone-form-filename"><?= htmlspecialchars($filename ?? '', ENT_QUOTES, 'UTF-8') ?></div>
|
||||
<div class="dropzone-form-loading-info">
|
||||
<div class="dropzone-form-loading-info-col">
|
||||
<div class="dropzone-form-loading-info-item">
|
||||
<div class="dropzone-form-loading-info-icon">
|
||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 4.5V6.5H4M6 20.5V18.5H4M18 4.5V6.5H20M18 20.5V18.5H20M7 7.5H17V17.5H7V7.5Z" stroke="currentColor" stroke-linecap="square"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="dropzone-form-loading-info-text">
|
||||
<span id="origin_width"><?= htmlspecialchars($origin_width ?? '', ENT_QUOTES, 'UTF-8') ?></span> x <span id="origin_height"><?= htmlspecialchars($origin_height ?? '', ENT_QUOTES, 'UTF-8') ?></span> <span>px</span></div>
|
||||
</div>
|
||||
<div class="dropzone-form-loading-info-item">
|
||||
<div class="dropzone-form-loading-info-icon">
|
||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 17.5C18.7614 17.5 21 15.2614 21 12.5C21 10.017 19.19 7.95665 16.8174 7.56649C16.8136 7.56585 16.8104 7.56299 16.8094 7.5592C16.1762 5.22047 14.039 3.5 11.5 3.5C8.46243 3.5 6 5.96243 6 9C6 9.20572 6.01129 9.4088 6.0333 9.60866C6.03385 9.61364 6.0306 9.61827 6.02574 9.61949C4.28747 10.0545 3 11.6269 3 13.5C3 15.7091 4.79086 17.5 7 17.5H8M12 10.5047V21.5M12 10.5047L9 13.5M12 10.5047L15 13.5" stroke="currentColor" stroke-linecap="square"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="dropzone-form-loading-info-text">
|
||||
<span id="origin_size"><?= htmlspecialchars($origin_size ?? '', ENT_QUOTES, 'UTF-8') ?></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropzone-form-loading-info-col dropzone-form-loading-info-arrow">
|
||||
<svg width="37" height="24" viewBox="0 0 37 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 17L12 12L7 7M13 17L18 12L13 7" stroke="currentColor" stroke-linecap="square"/>
|
||||
<path d="M20 17L25 12L20 7M26 17L31 12L26 7" stroke="currentColor" stroke-linecap="square"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="dropzone-form-loading-info-col">
|
||||
<div class="dropzone-form-loading-info-item">
|
||||
<div class="dropzone-form-loading-info-icon">
|
||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 4.5V6.5H4M6 20.5V18.5H4M18 4.5V6.5H20M18 20.5V18.5H20M7 7.5H17V17.5H7V7.5Z" stroke="currentColor" stroke-linecap="square"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="dropzone-form-loading-info-text">
|
||||
<span id="upscale_width"><?= htmlspecialchars($upscale_width ?? '', ENT_QUOTES, 'UTF-8') ?></span> x <span id="upscale_height"><?= htmlspecialchars($upscale_height ?? '', ENT_QUOTES, 'UTF-8') ?></span> <span>px</span></div>
|
||||
</div>
|
||||
<div class="dropzone-form-loading-info-item">
|
||||
<div class="dropzone-form-loading-info-icon">
|
||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16 17.5C18.7614 17.5 21 15.2614 21 12.5C21 10.017 19.19 7.95665 16.8174 7.56649C16.8136 7.56585 16.8104 7.56299 16.8094 7.5592C16.1762 5.22047 14.039 3.5 11.5 3.5C8.46243 3.5 6 5.96243 6 9C6 9.20572 6.01129 9.4088 6.0333 9.60866C6.03385 9.61364 6.0306 9.61827 6.02574 9.61949C4.28747 10.0545 3 11.6269 3 13.5C3 15.7091 4.79086 17.5 7 17.5H8M12 10.5047V21.5M12 10.5047L9 13.5M12 10.5047L15 13.5" stroke="currentColor" stroke-linecap="square"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="dropzone-form-loading-info-text">
|
||||
≈ <span id="upscale_size"><?= htmlspecialchars($upscale_size ?? '', ENT_QUOTES, 'UTF-8') ?></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dropzone-form-loading-progress">
|
||||
<div class="progress">
|
||||
<div class="progress-label">0%</div>
|
||||
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dropzone-form-note"><?= Yii::t('app', 'Увеличение 4х доступно после регистрации') ?></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const labels = <?= $labelsJson ?>;
|
||||
|
||||
const process = {
|
||||
el: document.getElementById('process'),
|
||||
|
||||
barEl: document.querySelector('.progress-bar'),
|
||||
labelEl: document.querySelector('.progress-label'),
|
||||
|
||||
titleEl: document.getElementById('process-title'),
|
||||
filenameEl: document.getElementById('process-filename'),
|
||||
|
||||
originSizeEl: document.getElementById('origin_size'),
|
||||
upscaleSizeEl: document.getElementById('upscale_size'),
|
||||
originWidthEl: document.getElementById('origin_width'),
|
||||
originHeightEl: document.getElementById('origin_height'),
|
||||
upscaleWidthEl: document.getElementById('upscale_width'),
|
||||
upscaleHeightEl: document.getElementById('upscale_height'),
|
||||
|
||||
status: {
|
||||
current: 0,
|
||||
stage: 0,
|
||||
},
|
||||
|
||||
hide() {
|
||||
if (this.el) this.el.style.display = 'none';
|
||||
},
|
||||
show() {
|
||||
if (this.el) this.el.style.display = '';
|
||||
},
|
||||
setFilename(filename) {
|
||||
if (this.filenameEl) this.filenameEl.innerText = filename;
|
||||
},
|
||||
setSize(size) {
|
||||
if (this.originSizeEl) this.originSizeEl.innerText = this.formatB(size);
|
||||
if (this.upscaleSizeEl) this.upscaleSizeEl.innerText = this.formatB(size * 3);
|
||||
},
|
||||
setDimensions(w, h) {
|
||||
if (this.originWidthEl) this.originWidthEl.innerText = w;
|
||||
if (this.originHeightEl) this.originHeightEl.innerText = h;
|
||||
if (this.upscaleWidthEl) this.upscaleWidthEl.innerText = w * 2;
|
||||
if (this.upscaleHeightEl) this.upscaleHeightEl.innerText = h * 2;
|
||||
},
|
||||
setStage(stage) {
|
||||
if (!this.titleEl) return;
|
||||
this.status.stage = stage;
|
||||
this.titleEl.innerText = labels['stage' + stage] || '...';
|
||||
},
|
||||
setStatus(status) {
|
||||
const val = Math.min(100, Math.max(0, status));
|
||||
this.status.current = val;
|
||||
if (this.barEl) this.barEl.style.width = val + '%';
|
||||
if (this.labelEl) this.labelEl.innerText = val + '%';
|
||||
},
|
||||
|
||||
formatB(bytes) {
|
||||
if (bytes === 0 || isNaN(bytes)) return '0 B';
|
||||
const units = ['B', 'Kb', 'Mb', 'Gb', 'Tb'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
const formattedValue = (bytes / Math.pow(1024, i)).toFixed(2);
|
||||
return parseFloat(formattedValue) + ' ' + units[i];
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"host": "zoomzilla.ru",
|
||||
|
||||
"utm": {
|
||||
"indpic": {
|
||||
"weights": {
|
||||
"1": 0.2,
|
||||
"2": 0.3
|
||||
},
|
||||
"default": "1"
|
||||
},
|
||||
"theme": {
|
||||
"weights": {
|
||||
"light": 0.5,
|
||||
"dark": 0.5
|
||||
},
|
||||
"default": "dark"
|
||||
},
|
||||
"degr": {
|
||||
"weights": {
|
||||
"1": 0.7,
|
||||
"2": 0.3
|
||||
},
|
||||
"default": "1"
|
||||
},
|
||||
"registration": {
|
||||
"weights": {
|
||||
"before": 0.7,
|
||||
"after": 0.3
|
||||
},
|
||||
"default": "before"
|
||||
},
|
||||
"bchoice": {
|
||||
"weights": {
|
||||
"a": 0.7,
|
||||
"b": 0.3
|
||||
},
|
||||
"default": "a"
|
||||
},
|
||||
"chb": {
|
||||
"weights": {
|
||||
"a": 0.7,
|
||||
"b": 0.3
|
||||
},
|
||||
"default": "a"
|
||||
},
|
||||
"tp": {
|
||||
"weights": {
|
||||
"1": 0.7,
|
||||
"2": 0.3
|
||||
},
|
||||
"default": "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="58" viewBox="0 0 252 58" fill="none">
|
||||
|
||||
<path d="M112.076 8.8L103.832 34H97.568L92.024 16.936L86.3 34H80.072L71.792 8.8H77.84L83.528 26.512L89.468 8.8H94.868L100.628 26.656L106.496 8.8H112.076ZM133.836 20.896C135.276 21.352 136.404 22.12 137.22 23.2C138.036 24.256 138.444 25.564 138.444 27.124C138.444 29.332 137.58 31.036 135.852 32.236C134.148 33.412 131.652 34 128.364 34H115.332V8.8H127.644C130.716 8.8 133.068 9.388 134.7 10.564C136.356 11.74 137.184 13.336 137.184 15.352C137.184 16.576 136.884 17.668 136.284 18.628C135.708 19.588 134.892 20.344 133.836 20.896ZM121.128 13.192V19.132H126.924C128.364 19.132 129.456 18.88 130.2 18.376C130.944 17.872 131.316 17.128 131.316 16.144C131.316 15.16 130.944 14.428 130.2 13.948C129.456 13.444 128.364 13.192 126.924 13.192H121.128ZM127.932 29.608C129.468 29.608 130.62 29.356 131.388 28.852C132.18 28.348 132.576 27.568 132.576 26.512C132.576 24.424 131.028 23.38 127.932 23.38H121.128V29.608H127.932ZM162.371 29.32V34H142.859V8.8H161.903V13.48H148.655V18.952H160.355V23.488H148.655V29.32H162.371ZM185.015 8.8V12.58L175.655 34H169.319L178.355 13.552H170.003V17.656H164.819V8.8H185.015ZM197.708 34.432C195.62 34.432 193.76 33.916 192.128 32.884C190.496 31.852 189.212 30.364 188.276 28.42C187.364 26.452 186.908 24.112 186.908 21.4C186.908 18.688 187.364 16.36 188.276 14.416C189.212 12.448 190.496 10.948 192.128 9.916C193.76 8.884 195.62 8.368 197.708 8.368C199.796 8.368 201.656 8.884 203.288 9.916C204.92 10.948 206.192 12.448 207.104 14.416C208.04 16.36 208.508 18.688 208.508 21.4C208.508 24.112 208.04 26.452 207.104 28.42C206.192 30.364 204.92 31.852 203.288 32.884C201.656 33.916 199.796 34.432 197.708 34.432ZM197.708 29.5C199.244 29.5 200.444 28.84 201.308 27.52C202.196 26.2 202.64 24.16 202.64 21.4C202.64 18.64 202.196 16.6 201.308 15.28C200.444 13.96 199.244 13.3 197.708 13.3C196.196 13.3 194.996 13.96 194.108 15.28C193.244 16.6 192.812 18.64 192.812 21.4C192.812 24.16 193.244 26.2 194.108 27.52C194.996 28.84 196.196 29.5 197.708 29.5Z" fill="url(#paint0_linear)"/>
|
||||
<g clip-path="url(#clip0)">
|
||||
<g opacity="0.2">
|
||||
<path opacity="0.2" d="M11.7632 38.9239C12.9756 37.7274 14.5725 36.9859 16.2788 36.8271C15.1981 36.7236 14.1075 36.858 13.0857 37.2205C12.0638 37.5831 11.136 38.1648 10.3689 38.9239L14.2502 42.7577C14.4826 42.5282 14.7338 42.3111 14.985 42.1374L11.7632 38.9239Z" fill="#BDBDBD"/>
|
||||
</g>
|
||||
<path d="M20.7944 38.9239C21.1486 39.2786 21.4663 39.6672 21.7427 40.084C20.3716 39.9581 18.9893 40.1324 17.6942 40.5946C16.3992 41.0567 15.2232 41.7953 14.2502 42.7577L10.3689 38.9239C11.7524 37.5601 13.6271 36.7942 15.5816 36.7942C17.5362 36.7942 19.4109 37.5601 20.7944 38.9239V38.9239ZM62 26.07L37.6321 2L31.1068 8.45176L50.1615 27.2735C50.3496 27.4585 50.499 27.6785 50.6009 27.9207C50.7028 28.1629 50.7552 28.4226 50.7552 28.6848C50.7552 28.9471 50.7028 29.2068 50.6009 29.449C50.499 29.6912 50.3496 29.9111 50.1615 30.0962L30.2589 49.7678C30.1549 52.0743 29.1803 54.2588 27.5269 55.8908L29.6748 58.0124L62 26.07Z" fill="url(#paint1_linear)"/>
|
||||
<path opacity="0.2" d="M28.8521 55.8784C30.5026 54.2495 31.477 52.07 31.5841 49.7678L51.4992 30.0961C51.8762 29.7208 52.0877 29.2135 52.0877 28.6848C52.0877 28.1561 51.8762 27.6488 51.4992 27.2735L32.4257 8.45174L36.3258 4.5993C36.866 4.06706 37.5588 3.71186 38.3104 3.58191C37.7194 3.47529 37.1111 3.51219 36.5378 3.68942C35.9645 3.86666 35.4434 4.17892 35.0195 4.5993L31.1068 8.45174L50.1615 27.2735C50.3497 27.4585 50.499 27.6785 50.6009 27.9207C50.7028 28.1629 50.7552 28.4225 50.7552 28.6848C50.7552 28.9471 50.7028 29.2068 50.6009 29.449C50.499 29.6912 50.3497 29.9111 50.1615 30.0961L30.2589 49.7678C30.1549 52.0743 29.1803 54.2588 27.5269 55.8908L29.6748 58.0124L30.3406 57.392L28.8521 55.8784Z" fill="#BDBDBD"/>
|
||||
<path d="M29.6748 9.85997L24.3679 4.61792L0 28.6755L5.30693 33.9176L8.93699 37.5033C10.7386 35.9704 13.0603 35.1672 15.4364 35.2547C17.8125 35.3423 20.0673 36.3141 21.7482 37.9753C23.4291 39.6365 24.412 41.8641 24.4995 44.2112C24.587 46.5583 23.7727 48.8513 22.22 50.6301L24.3679 52.7517L30.0203 47.1685L48.7358 28.6755L29.6748 9.85997Z" fill="#7241E2"/>
|
||||
<g opacity="0.15">
|
||||
<path opacity="0.15" d="M23.4196 50.6301C24.77 49.2981 25.6772 47.5909 26.0199 45.7365C26.3627 43.882 26.1248 41.9683 25.338 40.2509C24.5512 38.5334 23.2529 37.0937 21.6163 36.124C19.9798 35.1542 18.0827 34.7005 16.1783 34.8233C17.9807 34.9354 19.712 35.5589 21.1645 36.619C22.617 37.6791 23.7289 39.1306 24.3668 40.7995C25.0047 42.4684 25.1414 44.2837 24.7606 46.0274C24.3798 47.7712 23.4977 49.3694 22.22 50.6301L24.3679 52.7518L24.996 52.1314L23.4196 50.6301Z" fill="#BDBDBD"/>
|
||||
<path opacity="0.15" d="M6.51276 33.93L1.19955 28.6755L22.9423 7.21103C23.4931 6.66825 24.2027 6.31018 24.9708 6.18744C24.3882 6.09254 23.791 6.13587 23.2286 6.31384C22.6662 6.49182 22.1548 6.79934 21.7364 7.21103L0 28.6755L5.30693 33.9176L8.93699 37.5033C9.13577 37.3039 9.34543 37.1155 9.56503 36.9387L6.51276 33.93Z" fill="#BDBDBD"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="71" y1="21.5" x2="210" y2="21.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7241E2"/>
|
||||
<stop offset="1" stop-color="#F413B5"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="10.3689" y1="30.0062" x2="62" y2="30.0062" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#662AF0"/>
|
||||
<stop offset="1" stop-color="#EF1AB3"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0">
|
||||
<rect width="62" height="56" fill="white" transform="translate(0 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,29 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="252" height="58" viewBox="0 0 252 58" fill="none">
|
||||
|
||||
<path d="M112.076 8.8L103.832 34H97.568L92.024 16.936L86.3 34H80.072L71.792 8.8H77.84L83.528 26.512L89.468 8.8H94.868L100.628 26.656L106.496 8.8H112.076ZM133.836 20.896C135.276 21.352 136.404 22.12 137.22 23.2C138.036 24.256 138.444 25.564 138.444 27.124C138.444 29.332 137.58 31.036 135.852 32.236C134.148 33.412 131.652 34 128.364 34H115.332V8.8H127.644C130.716 8.8 133.068 9.388 134.7 10.564C136.356 11.74 137.184 13.336 137.184 15.352C137.184 16.576 136.884 17.668 136.284 18.628C135.708 19.588 134.892 20.344 133.836 20.896ZM121.128 13.192V19.132H126.924C128.364 19.132 129.456 18.88 130.2 18.376C130.944 17.872 131.316 17.128 131.316 16.144C131.316 15.16 130.944 14.428 130.2 13.948C129.456 13.444 128.364 13.192 126.924 13.192H121.128ZM127.932 29.608C129.468 29.608 130.62 29.356 131.388 28.852C132.18 28.348 132.576 27.568 132.576 26.512C132.576 24.424 131.028 23.38 127.932 23.38H121.128V29.608H127.932ZM162.371 29.32V34H142.859V8.8H161.903V13.48H148.655V18.952H160.355V23.488H148.655V29.32H162.371ZM185.015 8.8V12.58L175.655 34H169.319L178.355 13.552H170.003V17.656H164.819V8.8H185.015ZM197.708 34.432C195.62 34.432 193.76 33.916 192.128 32.884C190.496 31.852 189.212 30.364 188.276 28.42C187.364 26.452 186.908 24.112 186.908 21.4C186.908 18.688 187.364 16.36 188.276 14.416C189.212 12.448 190.496 10.948 192.128 9.916C193.76 8.884 195.62 8.368 197.708 8.368C199.796 8.368 201.656 8.884 203.288 9.916C204.92 10.948 206.192 12.448 207.104 14.416C208.04 16.36 208.508 18.688 208.508 21.4C208.508 24.112 208.04 26.452 207.104 28.42C206.192 30.364 204.92 31.852 203.288 32.884C201.656 33.916 199.796 34.432 197.708 34.432ZM197.708 29.5C199.244 29.5 200.444 28.84 201.308 27.52C202.196 26.2 202.64 24.16 202.64 21.4C202.64 18.64 202.196 16.6 201.308 15.28C200.444 13.96 199.244 13.3 197.708 13.3C196.196 13.3 194.996 13.96 194.108 15.28C193.244 16.6 192.812 18.64 192.812 21.4C192.812 24.16 193.244 26.2 194.108 27.52C194.996 28.84 196.196 29.5 197.708 29.5Z" fill="url(#paint0_linear)"/>
|
||||
<g clip-path="url(#clip0)">
|
||||
<g opacity="0.2">
|
||||
<path opacity="0.2" d="M11.7632 38.9239C12.9756 37.7274 14.5725 36.9859 16.2788 36.8271C15.1981 36.7236 14.1075 36.858 13.0857 37.2205C12.0638 37.5831 11.136 38.1648 10.3689 38.9239L14.2502 42.7577C14.4826 42.5282 14.7338 42.3111 14.985 42.1374L11.7632 38.9239Z" fill="#BDBDBD"/>
|
||||
</g>
|
||||
<path d="M20.7944 38.9239C21.1486 39.2786 21.4663 39.6672 21.7427 40.084C20.3716 39.9581 18.9893 40.1324 17.6942 40.5946C16.3992 41.0567 15.2232 41.7953 14.2502 42.7577L10.3689 38.9239C11.7524 37.5601 13.6271 36.7942 15.5816 36.7942C17.5362 36.7942 19.4109 37.5601 20.7944 38.9239V38.9239ZM62 26.07L37.6321 2L31.1068 8.45176L50.1615 27.2735C50.3496 27.4585 50.499 27.6785 50.6009 27.9207C50.7028 28.1629 50.7552 28.4226 50.7552 28.6848C50.7552 28.9471 50.7028 29.2068 50.6009 29.449C50.499 29.6912 50.3496 29.9111 50.1615 30.0962L30.2589 49.7678C30.1549 52.0743 29.1803 54.2588 27.5269 55.8908L29.6748 58.0124L62 26.07Z" fill="url(#paint1_linear)"/>
|
||||
<path opacity="0.2" d="M28.8521 55.8784C30.5026 54.2495 31.477 52.07 31.5841 49.7678L51.4992 30.0961C51.8762 29.7208 52.0877 29.2135 52.0877 28.6848C52.0877 28.1561 51.8762 27.6488 51.4992 27.2735L32.4257 8.45174L36.3258 4.5993C36.866 4.06706 37.5588 3.71186 38.3104 3.58191C37.7194 3.47529 37.1111 3.51219 36.5378 3.68942C35.9645 3.86666 35.4434 4.17892 35.0195 4.5993L31.1068 8.45174L50.1615 27.2735C50.3497 27.4585 50.499 27.6785 50.6009 27.9207C50.7028 28.1629 50.7552 28.4225 50.7552 28.6848C50.7552 28.9471 50.7028 29.2068 50.6009 29.449C50.499 29.6912 50.3497 29.9111 50.1615 30.0961L30.2589 49.7678C30.1549 52.0743 29.1803 54.2588 27.5269 55.8908L29.6748 58.0124L30.3406 57.392L28.8521 55.8784Z" fill="#BDBDBD"/>
|
||||
<path d="M29.6748 9.85997L24.3679 4.61792L0 28.6755L5.30693 33.9176L8.93699 37.5033C10.7386 35.9704 13.0603 35.1672 15.4364 35.2547C17.8125 35.3423 20.0673 36.3141 21.7482 37.9753C23.4291 39.6365 24.412 41.8641 24.4995 44.2112C24.587 46.5583 23.7727 48.8513 22.22 50.6301L24.3679 52.7517L30.0203 47.1685L48.7358 28.6755L29.6748 9.85997Z" fill="#7241E2"/>
|
||||
<g opacity="0.15">
|
||||
<path opacity="0.15" d="M23.4196 50.6301C24.77 49.2981 25.6772 47.5909 26.0199 45.7365C26.3627 43.882 26.1248 41.9683 25.338 40.2509C24.5512 38.5334 23.2529 37.0937 21.6163 36.124C19.9798 35.1542 18.0827 34.7005 16.1783 34.8233C17.9807 34.9354 19.712 35.5589 21.1645 36.619C22.617 37.6791 23.7289 39.1306 24.3668 40.7995C25.0047 42.4684 25.1414 44.2837 24.7606 46.0274C24.3798 47.7712 23.4977 49.3694 22.22 50.6301L24.3679 52.7518L24.996 52.1314L23.4196 50.6301Z" fill="#BDBDBD"/>
|
||||
<path opacity="0.15" d="M6.51276 33.93L1.19955 28.6755L22.9423 7.21103C23.4931 6.66825 24.2027 6.31018 24.9708 6.18744C24.3882 6.09254 23.791 6.13587 23.2286 6.31384C22.6662 6.49182 22.1548 6.79934 21.7364 7.21103L0 28.6755L5.30693 33.9176L8.93699 37.5033C9.13577 37.3039 9.34543 37.1155 9.56503 36.9387L6.51276 33.93Z" fill="#BDBDBD"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="71" y1="21.5" x2="210" y2="21.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7241E2"/>
|
||||
<stop offset="1" stop-color="#F413B5"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="10.3689" y1="30.0062" x2="62" y2="30.0062" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#662AF0"/>
|
||||
<stop offset="1" stop-color="#EF1AB3"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0">
|
||||
<rect width="62" height="56" fill="white" transform="translate(0 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 754 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
@@ -0,0 +1,24 @@
|
||||
<svg viewBox="0 0 47 47" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.18" filter="url(#filter0_f_234_1209)">
|
||||
<path d="M23.5 37C30.9558 37 37 30.9558 37 23.5C37 16.0442 30.9558 10 23.5 10C16.0442 10 10 16.0442 10 23.5C10 30.9558 16.0442 37 23.5 37Z" fill="url(#paint0_linear_234_1209)"/>
|
||||
</g>
|
||||
<path d="M8.11628 23.5C11.4128 16.2162 16.8442 12.5116 23.5 12.5116C30.1558 12.5116 35.5872 16.2162 38.8837 23.5C35.5872 30.7837 30.1558 34.4883 23.5 34.4883C16.8442 34.4883 11.4128 30.7837 8.11628 23.5Z" fill="#090812" stroke="url(#paint1_linear_234_1209)" stroke-width="2.6" stroke-linejoin="round"/>
|
||||
<path d="M23.5 29.7791C26.9678 29.7791 29.7791 26.9679 29.7791 23.5C29.7791 20.0322 26.9678 17.2209 23.5 17.2209C20.0322 17.2209 17.2209 20.0322 17.2209 23.5C17.2209 26.9679 20.0322 29.7791 23.5 29.7791Z" fill="url(#paint2_linear_234_1209)"/>
|
||||
<path d="M23.5 26.3256C25.0605 26.3256 26.3256 25.0605 26.3256 23.5C26.3256 21.9395 25.0605 20.6744 23.5 20.6744C21.9395 20.6744 20.6744 21.9395 20.6744 23.5C20.6744 25.0605 21.9395 26.3256 23.5 26.3256Z" fill="#090812"/>
|
||||
<path d="M28.8372 15.0233L29.936 17.6919L32.6047 18.7908L29.936 19.8896L28.8372 22.5582L27.7384 19.8896L25.0698 18.7908L27.7384 17.6919L28.8372 15.0233Z" fill="white"/>
|
||||
<path d="M14.7093 27.2675H13.7674C13.4207 27.2675 13.1395 27.5486 13.1395 27.8954V28.8372C13.1395 29.184 13.4207 29.4651 13.7674 29.4651H14.7093C15.0561 29.4651 15.3372 29.184 15.3372 28.8372V27.8954C15.3372 27.5486 15.0561 27.2675 14.7093 27.2675Z" fill="#6AA7FF"/>
|
||||
<path d="M18.9477 29.1511H17.6919C17.2584 29.1511 16.907 29.5025 16.907 29.936V31.1918C16.907 31.6253 17.2584 31.9767 17.6919 31.9767H18.9477C19.3812 31.9767 19.7326 31.6253 19.7326 31.1918V29.936C19.7326 29.5025 19.3812 29.1511 18.9477 29.1511Z" fill="#7B5CFF"/>
|
||||
<path d="M23.814 30.407H21.9302C21.4101 30.407 20.9884 30.8287 20.9884 31.3488V33.2326C20.9884 33.7527 21.4101 34.1744 21.9302 34.1744H23.814C24.3341 34.1744 24.7558 33.7527 24.7558 33.2326V31.3488C24.7558 30.8287 24.3341 30.407 23.814 30.407Z" fill="#D21BCE"/>
|
||||
<path d="M31.3488 29.7791L36.0581 34.4884" stroke="url(#paint3_linear_234_1209)" stroke-width="2.6" stroke-linecap="round"/>
|
||||
<defs>
|
||||
<filter id="filter0_f_234_1209" x="0" y="0" width="47" height="47" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="5" result="effect1_foregroundBlur_234_1209"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_234_1209" x1="7.80233" y1="9.05814" x2="40.4535" y2="38.5698" gradientUnits="userSpaceOnUse"><stop stop-color="#6AA7FF"/><stop offset="0.5" stop-color="#7B5CFF"/><stop offset="1" stop-color="#D21BCE"/></linearGradient>
|
||||
<linearGradient id="paint1_linear_234_1209" x1="7.80233" y1="9.05811" x2="40.4535" y2="38.5697" gradientUnits="userSpaceOnUse"><stop stop-color="#6AA7FF"/><stop offset="0.5" stop-color="#7B5CFF"/><stop offset="1" stop-color="#D21BCE"/></linearGradient>
|
||||
<linearGradient id="paint2_linear_234_1209" x1="7.80233" y1="9.05816" x2="40.4535" y2="38.5698" gradientUnits="userSpaceOnUse"><stop stop-color="#6AA7FF"/><stop offset="0.5" stop-color="#7B5CFF"/><stop offset="1" stop-color="#D21BCE"/></linearGradient>
|
||||
<linearGradient id="paint3_linear_234_1209" x1="7.80233" y1="9.05812" x2="40.4535" y2="38.5698" gradientUnits="userSpaceOnUse"><stop stop-color="#6AA7FF"/><stop offset="0.5" stop-color="#7B5CFF"/><stop offset="1" stop-color="#D21BCE"/></linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace {
|
||||
|
||||
class Yii
|
||||
{
|
||||
/** @var object */
|
||||
public static $app;
|
||||
|
||||
/** @var array<string, array<string, array<string, string>>> */
|
||||
private static array $messages = [];
|
||||
|
||||
/** @var string */
|
||||
public static string $language = 'ru';
|
||||
|
||||
/** @var array<string> */
|
||||
public static array $supportedLanguages = ['ru', 'en'];
|
||||
|
||||
/**
|
||||
* Initialize i18n and mock Yii::$app
|
||||
*
|
||||
* @param string $defaultLang
|
||||
*/
|
||||
public static function init(string $defaultLang = 'ru'): void
|
||||
{
|
||||
$lang = null;
|
||||
|
||||
// // 1. Language from query parameter (e.g. ?lang=en)
|
||||
// if (!empty($_GET['lang']) && is_string($_GET['lang'])) {
|
||||
// $candidate = strtolower(trim($_GET['lang']));
|
||||
// if (in_array($candidate, self::$supportedLanguages, true)) {
|
||||
// $lang = $candidate;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 2. Language from cookie
|
||||
// if ($lang === null && !empty($_COOKIE['lang']) && is_string($_COOKIE['lang'])) {
|
||||
// $candidate = strtolower(trim($_COOKIE['lang']));
|
||||
// if (in_array($candidate, self::$supportedLanguages, true)) {
|
||||
// $lang = $candidate;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 3. Language from Accept-Language header
|
||||
// if ($lang === null && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) && is_string($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
// $candidate = strtolower(substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2));
|
||||
// if (in_array($candidate, self::$supportedLanguages, true)) {
|
||||
// $lang = $candidate;
|
||||
// }
|
||||
// }
|
||||
|
||||
$lang = 'ru';
|
||||
|
||||
self::$language = $lang ?? $defaultLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a message to the specified language.
|
||||
*
|
||||
* @param string $category Message category (e.g. 'app', 'error')
|
||||
* @param string $message The message to be translated
|
||||
* @param array $params Parameters to replace in the message
|
||||
* @param string|null $language Target language, or null for current
|
||||
* @return string
|
||||
*/
|
||||
public static function t(string $category, string $message, array $params = [], ?string $language = null): string
|
||||
{
|
||||
$lang = $language ?: self::$language;
|
||||
|
||||
if (!isset(self::$messages[$lang][$category])) {
|
||||
$file = __DIR__ . '/messages/' . $lang . '/' . $category . '.php';
|
||||
if (file_exists($file)) {
|
||||
self::$messages[$lang][$category] = (array)(require $file);
|
||||
} else {
|
||||
self::$messages[$lang][$category] = [];
|
||||
}
|
||||
}
|
||||
|
||||
$translation = self::$messages[$lang][$category][$message] ?? $message;
|
||||
|
||||
// Handle Yii2 deprecated/empty marker
|
||||
if ($translation === '' || (is_string($translation) && strncmp($translation, '@@', 2) === 0)) {
|
||||
$translation = $message;
|
||||
}
|
||||
|
||||
if (!empty($params)) {
|
||||
$replace = [];
|
||||
foreach ($params as $key => $value) {
|
||||
$replace['{' . $key . '}'] = (string)$value;
|
||||
}
|
||||
$translation = strtr($translation, $replace);
|
||||
}
|
||||
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standalone shorthand helper for translations
|
||||
*/
|
||||
if (!function_exists('t')) {
|
||||
function t(string $category, string $message, array $params = [], ?string $language = null): string
|
||||
{
|
||||
return Yii::t($category, $message, $params, $language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 234 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M64 0V64H0V0H64Z" fill="white" fill-opacity="0.01"/>
|
||||
<path opacity="0.3" d="M58.6668 31.9999C58.6668 46.7275 46.7278 58.6666 32.0002 58.6666C17.2726 58.6666 5.3335 46.7275 5.3335 31.9999C5.3335 17.2723 17.2726 5.33325 32.0002 5.33325C46.7278 5.33325 58.6668 17.2723 58.6668 31.9999Z" fill="#E33B32"/>
|
||||
<path d="M26.6667 25.3333C26.6667 26.0697 26.0697 26.6667 25.3333 26.6667C24.597 26.6667 24 26.0697 24 25.3333C24 24.597 24.597 24 25.3333 24C26.0697 24 26.6667 24.597 26.6667 25.3333Z" fill="#E33B32"/>
|
||||
<path d="M42.6667 22.6667C42.6667 23.403 42.0697 24 41.3333 24C40.597 24 40 23.403 40 22.6667C40 21.9303 40.597 21.3333 41.3333 21.3333C42.0697 21.3333 42.6667 21.9303 42.6667 22.6667Z" fill="#E33B32"/>
|
||||
<path d="M29.3333 42.6666C32 38.6665 37.3333 35.9997 42.6667 37.3332M56 32C56 45.2548 45.2548 56 32 56C18.7452 56 8 45.2548 8 32C8 18.7452 18.7452 8 32 8C45.2548 8 56 18.7452 56 32ZM26.6667 25.3333C26.6667 26.0697 26.0697 26.6667 25.3333 26.6667C24.597 26.6667 24 26.0697 24 25.3333C24 24.597 24.597 24 25.3333 24C26.0697 24 26.6667 24.597 26.6667 25.3333ZM42.6667 22.6667C42.6667 23.403 42.0697 24 41.3333 24C40.597 24 40 23.403 40 22.6667C40 21.9303 40.597 21.3333 41.3333 21.3333C42.0697 21.3333 42.6667 21.9303 42.6667 22.6667Z" stroke="#E33B32" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 344 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 303 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 298 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 523 KiB |
|
After Width: | Height: | Size: 719 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 325 KiB |
|
After Width: | Height: | Size: 533 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 18 KiB |