Improve menu images, mobile links and sprite caching
This commit is contained in:
+63
-15
@@ -144,7 +144,8 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
|||||||
$output = $this->postProcess();
|
$output = $this->postProcess();
|
||||||
|
|
||||||
$this->context->controller->addCSS($this->_path . 'views/css/admin.css');
|
$this->context->controller->addCSS($this->_path . 'views/css/admin.css');
|
||||||
$this->context->controller->addJS($this->_path . 'views/js/admin.js');
|
$adminJsPath = $this->getLocalPath() . 'views/js/admin.js';
|
||||||
|
$this->context->controller->addJS($this->_path . 'views/js/admin.js?v=' . (is_file($adminJsPath) ? (string) filemtime($adminJsPath) : (string) time()));
|
||||||
|
|
||||||
$languages = $this->context->controller->getLanguages();
|
$languages = $this->context->controller->getLanguages();
|
||||||
$repository = new MenuRepository(Db::getInstance(), $this);
|
$repository = new MenuRepository(Db::getInstance(), $this);
|
||||||
@@ -155,7 +156,7 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
|||||||
'advmegamenu_default_lang' => (int) Configuration::get('PS_LANG_DEFAULT'),
|
'advmegamenu_default_lang' => (int) Configuration::get('PS_LANG_DEFAULT'),
|
||||||
'advmegamenu_tree' => $tree,
|
'advmegamenu_tree' => $tree,
|
||||||
'advmegamenu_ajax_url' => $this->getAdminAjaxUrl(),
|
'advmegamenu_ajax_url' => $this->getAdminAjaxUrl(),
|
||||||
'advmegamenu_admin_token' => Tools::getAdminTokenLite('AdminModules'),
|
'advmegamenu_admin_token' => $this->getAjaxToken(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $output . $this->display(__FILE__, 'views/templates/admin/configure.tpl');
|
return $output . $this->display(__FILE__, 'views/templates/admin/configure.tpl');
|
||||||
@@ -197,31 +198,42 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$menuCssFile = $this->getLocalPath() . 'views/css/megamenu.css';
|
||||||
|
$menuJsFile = $this->getLocalPath() . 'views/js/megamenu.js';
|
||||||
|
|
||||||
$controller->registerStylesheet(
|
$controller->registerStylesheet(
|
||||||
'module-advancedmegamenu-front',
|
'module-advancedmegamenu-front',
|
||||||
$this->_path . 'views/css/megamenu.css',
|
$this->_path . 'views/css/megamenu.css',
|
||||||
['media' => 'all', 'priority' => 150]
|
[
|
||||||
|
'media' => 'all',
|
||||||
|
'priority' => 150,
|
||||||
|
'version' => is_file($menuCssFile) ? (string) filemtime($menuCssFile) : $this->version,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
$controller->registerJavascript(
|
$controller->registerJavascript(
|
||||||
'module-advancedmegamenu-front',
|
'module-advancedmegamenu-front',
|
||||||
$this->_path . 'views/js/megamenu.js',
|
$this->_path . 'views/js/megamenu.js',
|
||||||
['position' => 'bottom', 'priority' => 150]
|
[
|
||||||
|
'position' => 'bottom',
|
||||||
|
'priority' => 150,
|
||||||
|
'version' => is_file($menuJsFile) ? (string) filemtime($menuJsFile) : $this->version,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
$spriteCssFile = $this->getLocalPath() . 'views/css/generated/menu-sprite.css';
|
$spriteCssFile = $this->getLocalPath() . 'views/css/generated/menu-sprite.css';
|
||||||
if (is_file($spriteCssFile)) {
|
if (is_file($spriteCssFile)) {
|
||||||
$spriteCssUrl = $this->_path . 'views/css/generated/menu-sprite.css';
|
$spriteCssUrl = $this->_path . 'views/css/generated/menu-sprite.css';
|
||||||
$spriteImageFile = $this->getLocalPath() . 'views/img/generated/menu-sprite.webp';
|
$spriteImageFile = $this->getCurrentSpriteImageFile($spriteCssFile);
|
||||||
$controller->registerStylesheet(
|
$controller->registerStylesheet(
|
||||||
'module-advancedmegamenu-sprite',
|
'module-advancedmegamenu-sprite',
|
||||||
$spriteCssUrl,
|
$spriteCssUrl,
|
||||||
['media' => 'all', 'priority' => 151]
|
['media' => 'all', 'priority' => 151, 'version' => (string) filemtime($spriteCssFile)]
|
||||||
);
|
);
|
||||||
|
|
||||||
$preloadLinks = [];
|
$preloadLinks = [];
|
||||||
|
|
||||||
if (is_file($spriteImageFile)) {
|
if ($spriteImageFile && is_file($spriteImageFile)) {
|
||||||
$spriteImageUrl = $this->_path . 'views/img/generated/menu-sprite.webp?v=' . (string) filemtime($spriteImageFile);
|
$spriteImageUrl = $this->_path . 'views/img/generated/' . basename($spriteImageFile);
|
||||||
$preloadLinks[] = '<link rel="preload" href="' . htmlspecialchars($spriteImageUrl, ENT_QUOTES, 'UTF-8') . '" as="image" type="image/webp">';
|
$preloadLinks[] = '<link rel="preload" href="' . htmlspecialchars($spriteImageUrl, ENT_QUOTES, 'UTF-8') . '" as="image" type="image/webp">';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,15 +418,22 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
|||||||
private function assertAdminToken(): void
|
private function assertAdminToken(): void
|
||||||
{
|
{
|
||||||
$token = (string) Tools::getValue('token');
|
$token = (string) Tools::getValue('token');
|
||||||
$expectedToken = Tools::getAdminTokenLite('AdminModules');
|
$validTokens = array_filter([
|
||||||
|
$this->getAjaxToken(),
|
||||||
|
Tools::getAdminTokenLite('AdminModules'),
|
||||||
|
]);
|
||||||
|
|
||||||
if (!hash_equals($expectedToken, $token)) {
|
foreach ($validTokens as $validToken) {
|
||||||
header('Content-Type: application/json', true, 403);
|
if (hash_equals((string) $validToken, $token)) {
|
||||||
exit(json_encode([
|
return;
|
||||||
'error' => true,
|
}
|
||||||
'message' => $this->trans('Invalid admin token.', [], 'Modules.Advancedmegamenu.Admin'),
|
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/json', true, 403);
|
||||||
|
exit(json_encode([
|
||||||
|
'error' => true,
|
||||||
|
'message' => $this->trans('Invalid admin token.', [], 'Modules.Advancedmegamenu.Admin'),
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function ajaxSearchProducts(): void
|
private function ajaxSearchProducts(): void
|
||||||
@@ -615,6 +634,28 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
|||||||
}, $icons));
|
}, $icons));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getCurrentSpriteImageFile(string $spriteCssFile): string
|
||||||
|
{
|
||||||
|
$css = (string) @file_get_contents($spriteCssFile);
|
||||||
|
if (preg_match('#img/generated/([^")]+\.webp)#', $css, $matches)) {
|
||||||
|
$spriteImageFile = $this->getLocalPath() . 'views/img/generated/' . basename($matches[1]);
|
||||||
|
if (is_file($spriteImageFile)) {
|
||||||
|
return $spriteImageFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$spriteFiles = glob($this->getLocalPath() . 'views/img/generated/menu-sprite*.webp') ?: [];
|
||||||
|
if (empty($spriteFiles)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($spriteFiles, static function (string $left, string $right): int {
|
||||||
|
return filemtime($right) <=> filemtime($left);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (string) $spriteFiles[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $node
|
* @param array<string, mixed> $node
|
||||||
*
|
*
|
||||||
@@ -970,6 +1011,13 @@ class AdvancedMegaMenu extends Module implements WidgetInterface
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getAjaxToken(): string
|
||||||
|
{
|
||||||
|
$employeeId = isset($this->context->employee) ? (int) $this->context->employee->id : 0;
|
||||||
|
|
||||||
|
return Tools::hash($this->name . '|ajax|' . $employeeId);
|
||||||
|
}
|
||||||
|
|
||||||
private function buildModuleImageUrl(string $path): string
|
private function buildModuleImageUrl(string $path): string
|
||||||
{
|
{
|
||||||
return rtrim($this->getPathUri(), '/') . '/views/img/' . ltrim($path, '/');
|
return rtrim($this->getPathUri(), '/') . '/views/img/' . ltrim($path, '/');
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class SpriteGenerator
|
|||||||
{
|
{
|
||||||
$imageOutputDir = $this->moduleDir . '/views/img/generated';
|
$imageOutputDir = $this->moduleDir . '/views/img/generated';
|
||||||
$cssOutputDir = $this->moduleDir . '/views/css/generated';
|
$cssOutputDir = $this->moduleDir . '/views/css/generated';
|
||||||
$spritePath = $imageOutputDir . '/menu-sprite.webp';
|
|
||||||
$cssPath = $cssOutputDir . '/menu-sprite.css';
|
$cssPath = $cssOutputDir . '/menu-sprite.css';
|
||||||
|
|
||||||
if (!is_dir($imageOutputDir)) {
|
if (!is_dir($imageOutputDir)) {
|
||||||
@@ -34,7 +33,7 @@ class SpriteGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($icons)) {
|
if (empty($icons)) {
|
||||||
$this->cleanup($spritePath, $cssPath);
|
$this->cleanup($imageOutputDir, $cssPath);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -77,7 +76,7 @@ class SpriteGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (empty($images) || $totalWidth === 0 || $maxHeight === 0) {
|
if (empty($images) || $totalWidth === 0 || $maxHeight === 0) {
|
||||||
$this->cleanup($spritePath, $cssPath);
|
$this->cleanup($imageOutputDir, $cssPath);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,22 +100,37 @@ class SpriteGenerator
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$spriteWritten = imagewebp($sprite, $spritePath, 85);
|
$tmpSpritePath = $imageOutputDir . '/menu-sprite.tmp.webp';
|
||||||
|
$spriteWritten = imagewebp($sprite, $tmpSpritePath, 85);
|
||||||
imagedestroy($sprite);
|
imagedestroy($sprite);
|
||||||
|
|
||||||
if (!$spriteWritten || !is_file($spritePath)) {
|
if (!$spriteWritten || !is_file($tmpSpritePath)) {
|
||||||
foreach ($images as $iconImage) {
|
foreach ($images as $iconImage) {
|
||||||
imagedestroy($iconImage['resource']);
|
imagedestroy($iconImage['resource']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cleanup($spritePath, $cssPath);
|
$this->cleanup($imageOutputDir, $cssPath);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = substr((string) sha1_file($tmpSpritePath), 0, 16);
|
||||||
|
$spriteFilename = 'menu-sprite-' . $hash . '.webp';
|
||||||
|
$spritePath = $imageOutputDir . '/' . $spriteFilename;
|
||||||
|
|
||||||
|
if (!rename($tmpSpritePath, $spritePath)) {
|
||||||
|
@unlink($tmpSpritePath);
|
||||||
|
foreach ($images as $iconImage) {
|
||||||
|
imagedestroy($iconImage['resource']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cleanup($imageOutputDir, $cssPath);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$version = is_file($spritePath) ? (string) filemtime($spritePath) : (string) time();
|
|
||||||
$css = [];
|
$css = [];
|
||||||
$css[] = '.adv-megamenu__icon{display:inline-block;background-repeat:no-repeat;background-image:url("../../img/generated/menu-sprite.webp?v=' . $version . '");}';
|
$css[] = '.adv-megamenu__icon{display:inline-block;background-repeat:no-repeat;background-image:url("../../img/generated/' . $spriteFilename . '");}';
|
||||||
|
|
||||||
foreach ($images as $iconImage) {
|
foreach ($images as $iconImage) {
|
||||||
$css[] = sprintf(
|
$css[] = sprintf(
|
||||||
@@ -132,15 +146,17 @@ class SpriteGenerator
|
|||||||
file_put_contents($cssPath, implode(PHP_EOL, $css) . PHP_EOL);
|
file_put_contents($cssPath, implode(PHP_EOL, $css) . PHP_EOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanup(string $spritePath, string $cssPath): void
|
private function cleanup(string $imageOutputDir, string $cssPath): void
|
||||||
{
|
{
|
||||||
if (is_file($spritePath)) {
|
|
||||||
unlink($spritePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_file($cssPath)) {
|
if (is_file($cssPath)) {
|
||||||
unlink($cssPath);
|
unlink($cssPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (glob($imageOutputDir . '/menu-sprite*.webp') ?: [] as $spritePath) {
|
||||||
|
if (is_file($spritePath)) {
|
||||||
|
unlink($spritePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -211,6 +211,19 @@
|
|||||||
border: 1px solid #edf1f4;
|
border: 1px solid #edf1f4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.adv-menu-image-preview-wrap {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adv-menu-image-remove {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
.adv-menu-admin-node__collections {
|
.adv-menu-admin-node__collections {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|||||||
@@ -569,6 +569,31 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-title-link,
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-title-link:link,
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-title-link:visited {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 0.25rem;
|
||||||
|
min-width: 0;
|
||||||
|
color: #8a3c1f;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
text-align: right;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adv-megamenu__mobile-title-link .material-icons {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-title-link:hover,
|
||||||
|
.adv-megamenu .adv-megamenu__mobile-title-link:focus-visible {
|
||||||
|
color: #8a3c1f;
|
||||||
|
}
|
||||||
|
|
||||||
.adv-megamenu__mobile-viewport {
|
.adv-megamenu__mobile-viewport {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: calc(100% - 56px);
|
height: calc(100% - 56px);
|
||||||
|
|||||||
+51
-10
@@ -458,7 +458,12 @@
|
|||||||
var rootHint = nodeModal.querySelector('.js-node-root-hint');
|
var rootHint = nodeModal.querySelector('.js-node-root-hint');
|
||||||
var imageGroup = nodeModal.querySelector('.js-node-image-group');
|
var imageGroup = nodeModal.querySelector('.js-node-image-group');
|
||||||
var previewWrap = nodeModal.querySelector('.js-node-icon-preview');
|
var previewWrap = nodeModal.querySelector('.js-node-icon-preview');
|
||||||
renderImagePreview(previewWrap, depth > 0 ? node.icon_url : '');
|
renderImagePreview(previewWrap, depth > 0 ? node.icon_url : '', function () {
|
||||||
|
node.icon_path = '';
|
||||||
|
node.icon_url = '';
|
||||||
|
renderImagePreview(previewWrap, '');
|
||||||
|
serializeTree();
|
||||||
|
});
|
||||||
|
|
||||||
rootHint.style.display = depth === 0 ? 'block' : 'none';
|
rootHint.style.display = depth === 0 ? 'block' : 'none';
|
||||||
rootHint.textContent = t('rootOnlyHint', 'For first-level items only title and custom link are edited here.');
|
rootHint.textContent = t('rootOnlyHint', 'For first-level items only title and custom link are edited here.');
|
||||||
@@ -473,7 +478,12 @@
|
|||||||
uploadImage(event.target.files[0], 'menu_icon', function (response) {
|
uploadImage(event.target.files[0], 'menu_icon', function (response) {
|
||||||
node.icon_path = response.path;
|
node.icon_path = response.path;
|
||||||
node.icon_url = response.url;
|
node.icon_url = response.url;
|
||||||
renderImagePreview(previewWrap, response.url);
|
renderImagePreview(previewWrap, response.url, function () {
|
||||||
|
node.icon_path = '';
|
||||||
|
node.icon_url = '';
|
||||||
|
renderImagePreview(previewWrap, '');
|
||||||
|
serializeTree();
|
||||||
|
});
|
||||||
serializeTree();
|
serializeTree();
|
||||||
}, {
|
}, {
|
||||||
menuDepth: depth,
|
menuDepth: depth,
|
||||||
@@ -572,7 +582,13 @@
|
|||||||
layoutModal.querySelector('.js-layout-type').value = layout.block_type || 'promo';
|
layoutModal.querySelector('.js-layout-type').value = layout.block_type || 'promo';
|
||||||
layoutModal.querySelector('.js-layout-title').checked = !!layout.show_title;
|
layoutModal.querySelector('.js-layout-title').checked = !!layout.show_title;
|
||||||
layoutModal.querySelector('.js-layout-bg').value = layout.background_color || '';
|
layoutModal.querySelector('.js-layout-bg').value = layout.background_color || '';
|
||||||
renderImagePreview(layoutModal.querySelector('.js-layout-preview'), layout.custom_image_url);
|
var layoutPreviewWrap = layoutModal.querySelector('.js-layout-preview');
|
||||||
|
renderImagePreview(layoutPreviewWrap, layout.custom_image_url, function () {
|
||||||
|
layout.custom_image = '';
|
||||||
|
layout.custom_image_url = '';
|
||||||
|
renderImagePreview(layoutPreviewWrap, '');
|
||||||
|
serializeTree();
|
||||||
|
});
|
||||||
renderProducts(layout.products, layoutModal.querySelector('.js-layout-products'));
|
renderProducts(layout.products, layoutModal.querySelector('.js-layout-products'));
|
||||||
layoutModal.querySelector('.js-layout-product-results').innerHTML = '';
|
layoutModal.querySelector('.js-layout-product-results').innerHTML = '';
|
||||||
layoutModal.querySelector('.js-layout-product-search').value = '';
|
layoutModal.querySelector('.js-layout-product-search').value = '';
|
||||||
@@ -583,7 +599,12 @@
|
|||||||
uploadImage(event.target.files[0], 'layout_image', function (response) {
|
uploadImage(event.target.files[0], 'layout_image', function (response) {
|
||||||
layout.custom_image = response.path;
|
layout.custom_image = response.path;
|
||||||
layout.custom_image_url = response.url;
|
layout.custom_image_url = response.url;
|
||||||
renderImagePreview(layoutModal.querySelector('.js-layout-preview'), response.url);
|
renderImagePreview(layoutPreviewWrap, response.url, function () {
|
||||||
|
layout.custom_image = '';
|
||||||
|
layout.custom_image_url = '';
|
||||||
|
renderImagePreview(layoutPreviewWrap, '');
|
||||||
|
serializeTree();
|
||||||
|
});
|
||||||
serializeTree();
|
serializeTree();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -678,16 +699,30 @@
|
|||||||
showModal(confirmModal);
|
showModal(confirmModal);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderImagePreview(wrap, url) {
|
function renderImagePreview(wrap, url, onRemove) {
|
||||||
wrap.innerHTML = '';
|
wrap.innerHTML = '';
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var preview = document.createElement('div');
|
||||||
|
preview.className = 'adv-menu-image-preview-wrap';
|
||||||
|
|
||||||
var image = document.createElement('img');
|
var image = document.createElement('img');
|
||||||
image.src = url;
|
image.src = url;
|
||||||
image.className = 'adv-menu-image-preview';
|
image.className = 'adv-menu-image-preview';
|
||||||
wrap.appendChild(image);
|
preview.appendChild(image);
|
||||||
|
|
||||||
|
if (typeof onRemove === 'function') {
|
||||||
|
var removeButton = document.createElement('button');
|
||||||
|
removeButton.type = 'button';
|
||||||
|
removeButton.className = 'btn btn-default btn-sm adv-menu-image-remove';
|
||||||
|
removeButton.innerHTML = '<i class="icon-trash"></i><span>' + escapeHtml(t('removeImage', 'Remove image')) + '</span>';
|
||||||
|
removeButton.addEventListener('click', onRemove);
|
||||||
|
preview.appendChild(removeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap.appendChild(preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodeTitle(node) {
|
function getNodeTitle(node) {
|
||||||
@@ -727,9 +762,6 @@
|
|||||||
|
|
||||||
var uploadOptions = options || {};
|
var uploadOptions = options || {};
|
||||||
var formData = new FormData();
|
var formData = new FormData();
|
||||||
formData.append('ajax', '1');
|
|
||||||
formData.append('action', 'uploadImage');
|
|
||||||
formData.append('token', token);
|
|
||||||
formData.append('preset', preset || 'default');
|
formData.append('preset', preset || 'default');
|
||||||
if (typeof uploadOptions.menuDepth !== 'undefined') {
|
if (typeof uploadOptions.menuDepth !== 'undefined') {
|
||||||
formData.append('menu_depth', String(uploadOptions.menuDepth));
|
formData.append('menu_depth', String(uploadOptions.menuDepth));
|
||||||
@@ -739,17 +771,26 @@
|
|||||||
}
|
}
|
||||||
formData.append('image', file);
|
formData.append('image', file);
|
||||||
|
|
||||||
fetch(ajaxUrl, {
|
fetch(ajaxUrl + '&ajax=1&action=uploadImage&token=' + encodeURIComponent(token), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Upload failed: HTTP ' + response.status);
|
||||||
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(function (payload) {
|
.then(function (payload) {
|
||||||
if (payload && !payload.error) {
|
if (payload && !payload.error) {
|
||||||
callback(payload);
|
callback(payload);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
throw new Error(payload && payload.message ? payload.message : 'Upload failed');
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
window.alert(error.message || 'Upload failed');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -219,14 +219,6 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
item.addEventListener('mouseenter', function () {
|
|
||||||
if (!isDesktop()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
activateSubmenuPane(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
bindDesktopClickIntent(link, function () {
|
bindDesktopClickIntent(link, function () {
|
||||||
activateSubmenuPane(item);
|
activateSubmenuPane(item);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"description": "{l s='Description' d='Modules.Advancedmegamenu.Admin' js=1}",
|
"description": "{l s='Description' d='Modules.Advancedmegamenu.Admin' js=1}",
|
||||||
"customLinkLabel": "{l s='Custom link' d='Modules.Advancedmegamenu.Admin' js=1}",
|
"customLinkLabel": "{l s='Custom link' d='Modules.Advancedmegamenu.Admin' js=1}",
|
||||||
"iconUpload": "{l s='Item image/icon' d='Modules.Advancedmegamenu.Admin' js=1}",
|
"iconUpload": "{l s='Item image/icon' d='Modules.Advancedmegamenu.Admin' js=1}",
|
||||||
|
"removeImage": "{l s='Remove image' d='Modules.Advancedmegamenu.Admin' js=1}",
|
||||||
"rootOnlyHint": "{l s='For first-level items only title and custom link are edited here.' d='Modules.Advancedmegamenu.Admin' js=1}",
|
"rootOnlyHint": "{l s='For first-level items only title and custom link are edited here.' d='Modules.Advancedmegamenu.Admin' js=1}",
|
||||||
"width": "{l s='Width' d='Modules.Advancedmegamenu.Admin' js=1}",
|
"width": "{l s='Width' d='Modules.Advancedmegamenu.Admin' js=1}",
|
||||||
"blockType": "{l s='Block type' d='Modules.Advancedmegamenu.Admin' js=1}",
|
"blockType": "{l s='Block type' d='Modules.Advancedmegamenu.Admin' js=1}",
|
||||||
|
|||||||
@@ -174,7 +174,14 @@
|
|||||||
<button type="button" class="adv-megamenu__mobile-back js-adv-back">
|
<button type="button" class="adv-megamenu__mobile-back js-adv-back">
|
||||||
< {l s='Wstecz' d='Modules.Advancedmegamenu.Shop'}
|
< {l s='Wstecz' d='Modules.Advancedmegamenu.Shop'}
|
||||||
</button>
|
</button>
|
||||||
<span>{$node.title|escape:'htmlall':'UTF-8'}</span>
|
{if $node.url && $node.url != '#'}
|
||||||
|
<a href="{$node.url|escape:'htmlall':'UTF-8'}" class="adv-megamenu__mobile-title-link" {if $node.new_window}target="_blank" rel="noopener"{/if}>
|
||||||
|
<span>{$node.title|escape:'htmlall':'UTF-8'}</span>
|
||||||
|
<span class="material-icons" aria-hidden="true">arrow_forward</span>
|
||||||
|
</a>
|
||||||
|
{else}
|
||||||
|
<span class="adv-megamenu__mobile-title">{$node.title|escape:'htmlall':'UTF-8'}</span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<ul class="adv-megamenu__mobile-list">
|
<ul class="adv-megamenu__mobile-list">
|
||||||
{foreach from=$node.category_branch item=branch}
|
{foreach from=$node.category_branch item=branch}
|
||||||
|
|||||||
Reference in New Issue
Block a user