(function () {
function boot() {
var root = document.getElementById('adv-megamenu-admin');
if (!root) {
return;
}
var treeContainer = root.querySelector('.js-tree');
var treeInput = document.getElementById('advmegamenu_tree_json');
var ajaxUrl = root.getAttribute('data-ajax-url');
var token = root.getAttribute('data-token');
var defaultLangId = String(root.getAttribute('data-default-lang') || '');
var languages = parseJson(root.querySelector('.js-adv-languages-data'), []);
var labels = parseJson(root.querySelector('.js-adv-i18n'), {});
var tree = parseJson(root.querySelector('.js-adv-tree-data'), []);
var tempId = Date.now();
var nodeModal = document.getElementById('advNodeModal');
var layoutModal = document.getElementById('advLayoutModal');
var confirmModal = document.getElementById('advConfirmModal');
var nodeModalState = null;
var layoutModalState = null;
var confirmAction = null;
var activeRootId = tree.length ? tree[0].id : null;
function nextId() {
tempId += 1;
return tempId;
}
function parseJson(element, fallback) {
if (!element || !element.textContent) {
return fallback;
}
try {
return JSON.parse(element.textContent);
} catch (error) {
return fallback;
}
}
function t(key, fallback) {
return labels[key] || fallback || key;
}
function showModal(element) {
if (window.jQuery && window.jQuery.fn && window.jQuery.fn.modal) {
window.jQuery(element).modal('show');
}
}
function hideModal(element) {
if (window.jQuery && window.jQuery.fn && window.jQuery.fn.modal) {
window.jQuery(element).modal('hide');
}
}
function createEmptyNode() {
var lang = {};
languages.forEach(function (language) {
lang[String(language.id_lang)] = {
title: '',
description: '',
custom_link: ''
};
});
return {
id: nextId(),
active: true,
type: 'link',
entity_id: 0,
icon_path: '',
icon_url: '',
new_window: false,
lang: lang,
layouts: [],
children: []
};
}
function createEmptyLayout() {
return {
id: nextId(),
column_width: 4,
show_title: true,
custom_image: '',
custom_image_url: '',
background_color: '',
block_type: 'promo',
products: []
};
}
function ensureNodeDefaults(node) {
if (!node.lang) {
node.lang = {};
}
if (!node.layouts) {
node.layouts = [];
}
if (!node.children) {
node.children = [];
}
languages.forEach(function (language) {
var langId = String(language.id_lang);
if (!node.lang[langId]) {
node.lang[langId] = {
title: '',
description: '',
custom_link: ''
};
}
});
}
function sanitizeTree(nodes, depth) {
nodes.forEach(function (node) {
ensureNodeDefaults(node);
if (depth === 0) {
node.icon_path = '';
node.icon_url = '';
Object.keys(node.lang).forEach(function (langId) {
node.lang[langId].description = '';
});
}
sanitizeTree(node.children || [], depth + 1);
});
}
function serializeTree() {
sanitizeTree(tree, 0);
treeInput.value = JSON.stringify(tree);
}
function render() {
serializeTree();
treeContainer.innerHTML = '';
if (!tree.length) {
var empty = document.createElement('div');
empty.className = 'adv-megamenu-admin__empty';
empty.textContent = t('emptyTree', 'No menu items yet.');
treeContainer.appendChild(empty);
return;
}
if (!findRootById(activeRootId)) {
activeRootId = tree[0].id;
}
var shell = document.createElement('div');
shell.className = 'adv-menu-admin-root-shell';
shell.innerHTML = '' +
'
' +
'';
var tabs = shell.querySelector('.adv-menu-admin-root-tabs');
var panel = shell.querySelector('.adv-menu-admin-root-panel');
tree.forEach(function (node, index) {
tabs.appendChild(renderRootTab(node, tree, index));
});
var activeRoot = findRootById(activeRootId) || tree[0];
if (activeRoot) {
panel.appendChild(renderNode(activeRoot, tree, getRootIndex(activeRoot.id), 0));
}
treeContainer.appendChild(shell);
}
function renderRootTab(node, collection, index) {
var button = document.createElement('button');
button.type = 'button';
button.className = 'adv-menu-admin-root-tab' + (node.id === activeRootId ? ' is-active' : '');
button.innerHTML = '' +
'' +
' ' +
' ' +
'' +
'';
button.addEventListener('click', function () {
activeRootId = node.id;
render();
});
button.querySelector('.js-root-edit').addEventListener('click', function (event) {
event.stopPropagation();
openNodeModal(node, 0);
});
button.querySelector('.js-root-up').addEventListener('click', function (event) {
event.stopPropagation();
move(collection, index, 'up');
});
button.querySelector('.js-root-down').addEventListener('click', function (event) {
event.stopPropagation();
move(collection, index, 'down');
});
button.querySelector('.js-root-delete').addEventListener('click', function (event) {
event.stopPropagation();
askConfirm(t('deleteNodeTitle', 'Delete menu item'), t('deleteNodeText', 'This will remove the selected menu item together with its children and attached blocks.'), function () {
collection.splice(index, 1);
if (activeRootId === node.id) {
activeRootId = collection.length ? collection[Math.max(0, index - 1)].id : null;
}
render();
});
});
return button;
}
function renderNode(node, collection, index, depth) {
ensureNodeDefaults(node);
var wrapper = document.createElement('section');
wrapper.className = 'adv-menu-admin-node' + (depth === 0 ? ' adv-menu-admin-node--root' : '') + (depth >= 2 ? ' adv-menu-admin-node--compact' : '');
var title = getNodeTitle(node) || t(depth === 0 ? 'rootItem' : 'childItem', 'Menu item');
var customLink = getNodeLink(node);
var imageHtml = depth > 0 && node.icon_url ? '' : '';
var meta = [];
meta.push(metaItem(node.type || 'link', 'icon-tag'));
if (!node.active) {
meta.push(metaItem(t('inactive', 'Inactive'), 'icon-ban-circle'));
}
if (node.new_window) {
meta.push(metaItem(t('newWindow', 'New window'), 'icon-external-link'));
}
meta.push(metaItem(String((node.children || []).length) + ' ' + t('children', 'children'), 'icon-sitemap'));
meta.push(metaItem(String((node.layouts || []).length) + ' ' + t('blocks', 'blocks'), 'icon-th-large'));
wrapper.innerHTML = '' +
'' +
'' +
' ' +
' ' +
'
';
bindNodeActions(wrapper, node, collection, index, depth);
var layoutsWrap = wrapper.querySelector('.adv-menu-admin-node__layouts-wrap');
if ((node.layouts || []).length) {
var layoutsTitle = document.createElement('h5');
layoutsTitle.className = 'adv-menu-admin-node__section-title';
layoutsTitle.innerHTML = '' + escapeHtml(t('blocks', 'blocks')) + '';
layoutsWrap.appendChild(layoutsTitle);
var layoutList = document.createElement('div');
layoutList.className = 'adv-menu-admin-layout-list';
node.layouts.forEach(function (layout, layoutIndex) {
layoutList.appendChild(renderLayout(node.layouts, layout, layoutIndex));
});
layoutsWrap.appendChild(layoutList);
}
var childrenWrap = wrapper.querySelector('.adv-menu-admin-node__children-wrap');
if ((node.children || []).length) {
var childrenTitle = document.createElement('h5');
childrenTitle.className = 'adv-menu-admin-node__section-title';
childrenTitle.innerHTML = '' + escapeHtml(t('children', 'children')) + '';
childrenWrap.appendChild(childrenTitle);
var childList = document.createElement('div');
childList.className = 'adv-menu-admin-children-list adv-menu-admin-node__children' + (depth >= 1 ? ' adv-menu-admin-node__children--grid' : '');
node.children.forEach(function (child, childIndex) {
childList.appendChild(renderNode(child, node.children, childIndex, depth + 1));
});
childrenWrap.appendChild(childList);
}
return wrapper;
}
function renderLayout(layouts, layout, index) {
var wrapper = document.createElement('section');
wrapper.className = 'adv-menu-admin-layout';
var meta = [];
meta.push(metaItem('col-' + (layout.column_width || 4), 'icon-columns'));
meta.push(metaItem(layout.block_type || 'promo', 'icon-th'));
if (layout.show_title) {
meta.push(metaItem(t('showTitle', 'Show title'), 'icon-font'));
}
if ((layout.products || []).length) {
meta.push(metaItem(String(layout.products.length) + ' ' + t('products', 'Products'), 'icon-shopping-cart'));
}
wrapper.innerHTML = '' +
'' +
'' +
(layout.custom_image_url ? '' : '') +
'
';
wrapper.querySelector('.js-layout-up').addEventListener('click', function () {
move(layouts, index, 'up');
});
wrapper.querySelector('.js-layout-down').addEventListener('click', function () {
move(layouts, index, 'down');
});
wrapper.querySelector('.js-layout-edit').addEventListener('click', function () {
openLayoutModal(layout);
});
wrapper.querySelector('.js-layout-delete').addEventListener('click', function () {
askConfirm(t('deleteLayoutTitle', 'Delete rich block'), t('deleteLayoutText', 'This will remove the selected rich content block.'), function () {
layouts.splice(index, 1);
render();
});
});
return wrapper;
}
function bindNodeActions(wrapper, node, collection, index, depth) {
wrapper.querySelector('.js-node-up').addEventListener('click', function () {
move(collection, index, 'up');
});
wrapper.querySelector('.js-node-down').addEventListener('click', function () {
move(collection, index, 'down');
});
wrapper.querySelector('.js-node-edit').addEventListener('click', function () {
openNodeModal(node, depth);
});
wrapper.querySelector('.js-node-add-child').addEventListener('click', function () {
var child = createEmptyNode();
openNodeModal(child, depth + 1, {
onSave: function (savedChild) {
node.children.push(savedChild);
}
});
});
wrapper.querySelector('.js-node-add-layout').addEventListener('click', function () {
var layout = createEmptyLayout();
openLayoutModal(layout, {
onSave: function (savedLayout) {
node.layouts.push(savedLayout);
}
});
});
wrapper.querySelector('.js-node-delete').addEventListener('click', function () {
askConfirm(t('deleteNodeTitle', 'Delete menu item'), t('deleteNodeText', 'This will remove the selected menu item together with its children and attached blocks.'), function () {
collection.splice(index, 1);
render();
});
});
}
function actionButton(className, icon, label) {
return '' +
'';
}
function metaItem(text, icon) {
return '';
}
function move(collection, index, direction) {
var target = direction === 'up' ? index - 1 : index + 1;
if (target < 0 || target >= collection.length) {
return;
}
var current = collection[index];
collection[index] = collection[target];
collection[target] = current;
if (collection === tree && current.id === activeRootId) {
activeRootId = current.id;
}
render();
}
function findRootById(id) {
for (var i = 0; i < tree.length; i += 1) {
if (tree[i].id === id) {
return tree[i];
}
}
return null;
}
function getRootIndex(id) {
for (var i = 0; i < tree.length; i += 1) {
if (tree[i].id === id) {
return i;
}
}
return 0;
}
function openNodeModal(node, depth, options) {
nodeModalState = {
node: node,
depth: depth,
onSave: options && typeof options.onSave === 'function' ? options.onSave : null
};
ensureNodeDefaults(node);
nodeModal.querySelector('.js-node-modal-title').textContent = t('nodeModalTitle', 'Edit menu item');
nodeModal.querySelector('.js-node-type').value = node.type || 'link';
nodeModal.querySelector('.js-node-entity-id').value = node.entity_id || 0;
nodeModal.querySelector('.js-node-active').checked = !!node.active;
nodeModal.querySelector('.js-node-new-window').checked = !!node.new_window;
var rootHint = nodeModal.querySelector('.js-node-root-hint');
var imageGroup = nodeModal.querySelector('.js-node-image-group');
var previewWrap = nodeModal.querySelector('.js-node-icon-preview');
renderImagePreview(previewWrap, depth > 0 ? node.icon_url : '');
rootHint.style.display = depth === 0 ? 'block' : 'none';
rootHint.textContent = t('rootOnlyHint', 'For first-level items only title and custom link are edited here.');
imageGroup.style.display = depth === 0 ? 'none' : 'block';
renderNodeLanguagePanels(node, depth);
var uploadInput = nodeModal.querySelector('.js-node-icon-upload');
uploadInput.value = '';
uploadInput.onchange = function (event) {
var currentType = nodeModal.querySelector('.js-node-type').value || node.type || 'link';
uploadImage(event.target.files[0], 'menu_icon', function (response) {
node.icon_path = response.path;
node.icon_url = response.url;
renderImagePreview(previewWrap, response.url);
serializeTree();
}, {
menuDepth: depth,
menuType: currentType
});
};
showModal(nodeModal);
}
function renderNodeLanguagePanels(node, depth) {
var wrap = nodeModal.querySelector('.js-node-languages');
wrap.innerHTML = '';
languages.forEach(function (language) {
var langId = String(language.id_lang);
var lang = node.lang[langId] || { title: '', description: '', custom_link: '' };
var panel = document.createElement('div');
panel.className = 'adv-megamenu-admin__lang-panel';
panel.innerHTML = '' +
'' + escapeHtml(language.iso_code + ' / ' + language.name) + '
' +
'';
panel.querySelector('.js-lang-title').addEventListener('input', function (event) {
node.lang[langId].title = event.target.value;
serializeTree();
});
if (depth > 0) {
panel.querySelector('.js-lang-description').addEventListener('input', function (event) {
node.lang[langId].description = event.target.value;
serializeTree();
});
}
panel.querySelector('.js-lang-link').addEventListener('input', function (event) {
node.lang[langId].custom_link = event.target.value;
serializeTree();
});
wrap.appendChild(panel);
});
}
function saveNodeModal() {
if (!nodeModalState) {
return;
}
var node = nodeModalState.node;
var depth = nodeModalState.depth;
var onSave = nodeModalState.onSave;
node.type = nodeModal.querySelector('.js-node-type').value;
node.entity_id = parseInt(nodeModal.querySelector('.js-node-entity-id').value || '0', 10);
node.active = nodeModal.querySelector('.js-node-active').checked;
node.new_window = nodeModal.querySelector('.js-node-new-window').checked;
if (depth === 0) {
node.icon_path = '';
node.icon_url = '';
Object.keys(node.lang).forEach(function (langId) {
node.lang[langId].description = '';
});
}
if (onSave) {
onSave(node);
}
hideModal(nodeModal);
nodeModalState = null;
render();
}
function openLayoutModal(layout, options) {
layoutModalState = {
layout: layout,
onSave: options && typeof options.onSave === 'function' ? options.onSave : null
};
layout.products = layout.products || [];
layoutModal.querySelector('.js-layout-width').value = layout.column_width || 4;
layoutModal.querySelector('.js-layout-type').value = layout.block_type || 'promo';
layoutModal.querySelector('.js-layout-title').checked = !!layout.show_title;
layoutModal.querySelector('.js-layout-bg').value = layout.background_color || '';
renderImagePreview(layoutModal.querySelector('.js-layout-preview'), layout.custom_image_url);
renderProducts(layout.products, layoutModal.querySelector('.js-layout-products'));
layoutModal.querySelector('.js-layout-product-results').innerHTML = '';
layoutModal.querySelector('.js-layout-product-search').value = '';
var uploadInput = layoutModal.querySelector('.js-layout-upload');
uploadInput.value = '';
uploadInput.onchange = function (event) {
uploadImage(event.target.files[0], 'layout_image', function (response) {
layout.custom_image = response.path;
layout.custom_image_url = response.url;
renderImagePreview(layoutModal.querySelector('.js-layout-preview'), response.url);
serializeTree();
});
};
bindLayoutProductSearch(layout);
showModal(layoutModal);
}
function bindLayoutProductSearch(layout) {
var resultsWrap = layoutModal.querySelector('.js-layout-product-results');
var searchInput = layoutModal.querySelector('.js-layout-product-search');
var searchTimer = null;
searchInput.oninput = function () {
clearTimeout(searchTimer);
var value = searchInput.value.trim();
if (value.length < 2) {
resultsWrap.innerHTML = '';
return;
}
searchTimer = window.setTimeout(function () {
fetch(ajaxUrl + '&ajax=1&action=searchProducts&token=' + encodeURIComponent(token) + '&q=' + encodeURIComponent(value))
.then(function (response) {
return response.json();
})
.then(function (payload) {
resultsWrap.innerHTML = '';
(payload.results || []).forEach(function (result) {
var button = document.createElement('button');
button.type = 'button';
button.textContent = result.label;
button.addEventListener('click', function () {
if (layout.products.indexOf(result.id) === -1) {
layout.products.push(result.id);
}
renderProducts(layout.products, layoutModal.querySelector('.js-layout-products'));
serializeTree();
});
resultsWrap.appendChild(button);
});
});
}, 200);
};
}
function renderProducts(products, wrap) {
wrap.innerHTML = '';
if (!products.length) {
wrap.innerHTML = '' + escapeHtml(t('noProducts', 'No products attached.')) + '';
return;
}
products.forEach(function (productId, index) {
var chip = document.createElement('span');
chip.className = 'adv-menu-product-chip';
chip.innerHTML = '#' + escapeHtml(String(productId)) + '';
chip.querySelector('button').addEventListener('click', function () {
products.splice(index, 1);
renderProducts(products, wrap);
serializeTree();
});
wrap.appendChild(chip);
});
}
function saveLayoutModal() {
if (!layoutModalState) {
return;
}
var layout = layoutModalState.layout;
var onSave = layoutModalState.onSave;
layout.column_width = parseInt(layoutModal.querySelector('.js-layout-width').value || '4', 10);
layout.block_type = layoutModal.querySelector('.js-layout-type').value;
layout.show_title = layoutModal.querySelector('.js-layout-title').checked;
layout.background_color = layoutModal.querySelector('.js-layout-bg').value;
if (onSave) {
onSave(layout);
}
hideModal(layoutModal);
layoutModalState = null;
render();
}
function askConfirm(title, text, callback) {
confirmAction = callback;
confirmModal.querySelector('.js-confirm-title').textContent = title;
confirmModal.querySelector('.js-confirm-text').textContent = text;
showModal(confirmModal);
}
function renderImagePreview(wrap, url) {
wrap.innerHTML = '';
if (!url) {
return;
}
var image = document.createElement('img');
image.src = url;
image.className = 'adv-menu-image-preview';
wrap.appendChild(image);
}
function getNodeTitle(node) {
if (defaultLangId && node.lang[defaultLangId] && node.lang[defaultLangId].title) {
return node.lang[defaultLangId].title;
}
var langIds = Object.keys(node.lang || {});
for (var i = 0; i < langIds.length; i += 1) {
if (node.lang[langIds[i]] && node.lang[langIds[i]].title) {
return node.lang[langIds[i]].title;
}
}
return '';
}
function getNodeLink(node) {
if (defaultLangId && node.lang[defaultLangId] && node.lang[defaultLangId].custom_link) {
return node.lang[defaultLangId].custom_link;
}
var langIds = Object.keys(node.lang || {});
for (var i = 0; i < langIds.length; i += 1) {
if (node.lang[langIds[i]] && node.lang[langIds[i]].custom_link) {
return node.lang[langIds[i]].custom_link;
}
}
return '';
}
function uploadImage(file, preset, callback, options) {
if (!file) {
return;
}
var uploadOptions = options || {};
var formData = new FormData();
formData.append('ajax', '1');
formData.append('action', 'uploadImage');
formData.append('token', token);
formData.append('preset', preset || 'default');
if (typeof uploadOptions.menuDepth !== 'undefined') {
formData.append('menu_depth', String(uploadOptions.menuDepth));
}
if (typeof uploadOptions.menuType !== 'undefined') {
formData.append('menu_type', String(uploadOptions.menuType));
}
formData.append('image', file);
fetch(ajaxUrl, {
method: 'POST',
body: formData
})
.then(function (response) {
return response.json();
})
.then(function (payload) {
if (payload && !payload.error) {
callback(payload);
}
});
}
function escapeHtml(value) {
return String(value)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function escapeAttribute(value) {
return escapeHtml(value).replace(/\n/g, '
');
}
root.querySelector('.js-add-root-node').addEventListener('click', function () {
var node = createEmptyNode();
openNodeModal(node, 0, {
onSave: function (savedNode) {
tree.push(savedNode);
activeRootId = savedNode.id;
}
});
});
nodeModal.querySelector('.js-save-node').addEventListener('click', saveNodeModal);
layoutModal.querySelector('.js-save-layout').addEventListener('click', saveLayoutModal);
confirmModal.querySelector('.js-confirm-action').addEventListener('click', function () {
if (typeof confirmAction === 'function') {
confirmAction();
}
hideModal(confirmModal);
confirmAction = null;
});
if (window.jQuery) {
window.jQuery(nodeModal).on('hidden.bs.modal', function () {
nodeModalState = null;
});
window.jQuery(layoutModal).on('hidden.bs.modal', function () {
layoutModalState = null;
});
window.jQuery(confirmModal).on('hidden.bs.modal', function () {
confirmAction = null;
});
}
render();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();