Files
advancedmegamenu/views/js/admin.js
T
tiamak 8a2163bb34
Build / Build & Release draft (push) Failing after 0s
PHP tests / PHP Syntax check 5.6 => 8.1 (push) Failing after 10s
PHP tests / PHP-CS-Fixer (push) Failing after 1m6s
PHP tests / PHPStan (1.7.1.2) (push) Failing after 2m27s
PHP tests / PHPStan (1.7.2.5) (push) Failing after 1m32s
PHP tests / PHPStan (1.7.3.4) (push) Failing after 1m29s
PHP tests / PHPStan (1.7.4.4) (push) Failing after 1m45s
PHP tests / PHPStan (1.7.5.1) (push) Failing after 1m47s
PHP tests / PHPStan (1.7.6) (push) Failing after 1m45s
PHP tests / PHPStan (1.7.7) (push) Failing after 2m0s
PHP tests / PHPStan (1.7.8) (push) Failing after 1m56s
PHP tests / PHPStan (latest) (push) Failing after 2m13s
Refine mega menu admin and responsive navigation
2026-04-13 13:34:38 +00:00

800 lines
29 KiB
JavaScript

(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 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 = '' +
'<div class="adv-menu-admin-root-tabs"></div>' +
'<div class="adv-menu-admin-root-panel"></div>';
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 = '' +
'<span class="adv-menu-admin-root-tab__main">' +
' <span class="adv-menu-admin-root-tab__title">' + escapeHtml(getNodeTitle(node) || t('rootItem', 'Root item')) + '</span>' +
' <span class="adv-menu-admin-root-tab__meta">' + escapeHtml(node.type || 'link') + '</span>' +
'</span>' +
'<span class="adv-menu-admin-root-tab__actions">' +
' <span class="btn btn-default btn-xs js-root-edit"><i class="icon-pencil"></i></span>' +
' <span class="btn btn-default btn-xs js-root-up"><i class="icon-arrow-left"></i></span>' +
' <span class="btn btn-default btn-xs js-root-down"><i class="icon-arrow-right"></i></span>' +
' <span class="btn btn-danger btn-xs js-root-delete"><i class="icon-trash"></i></span>' +
'</span>';
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 ? '<img src="' + escapeHtml(node.icon_url) + '" alt="" class="adv-menu-admin-node__image">' : '';
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 = '' +
'<div class="adv-menu-admin-node__header">' +
' <div class="adv-menu-admin-node__header-main">' +
' <h4 class="adv-menu-admin-node__title">' +
' <i class="icon-' + (depth === 0 ? 'star' : 'chevron-right') + '"></i>' +
' <span class="adv-menu-admin-node__title-text">' + escapeHtml(title) + '</span>' +
' </h4>' +
' <div class="adv-menu-admin-node__meta">' + meta.join('') + '</div>' +
' </div>' +
' <div class="adv-menu-admin-node__actions">' +
actionButton('js-node-up', 'icon-arrow-up', '') +
actionButton('js-node-down', 'icon-arrow-down', '') +
actionButton('js-node-edit', 'icon-pencil', t('edit', 'Edit')) +
actionButton('js-node-add-child', 'icon-level-down', t('addChild', 'Add child')) +
actionButton('js-node-add-layout', 'icon-th-large', t('addBlock', 'Add block')) +
actionButton('js-node-delete btn-danger', 'icon-trash', t('delete', 'Delete')) +
' </div>' +
'</div>' +
'<div class="adv-menu-admin-node__body">' +
' <div class="adv-menu-admin-node__summary">' +
' <div>' +
(customLink ? ' <div class="adv-menu-admin-node__link"><i class="icon-link"></i><span>' + escapeHtml(customLink) + '</span></div>' : '') +
(depth === 0 ? ' <p class="help-block">' + escapeHtml(t('rootRule', 'First level only stores title and custom link.')) + '</p>' : '') +
' </div>' +
' <div>' + imageHtml + '</div>' +
' </div>' +
' <div class="adv-menu-admin-node__collections">' +
' <div class="adv-menu-admin-node__layouts-wrap"></div>' +
' <div class="adv-menu-admin-node__children-wrap"></div>' +
' </div>' +
'</div>';
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 = '<i class="icon-th-large"></i><span>' + escapeHtml(t('blocks', 'blocks')) + '</span>';
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 = '<i class="icon-sitemap"></i><span>' + escapeHtml(t('children', 'children')) + '</span>';
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 = '' +
'<div class="adv-menu-admin-layout__header">' +
' <div class="adv-menu-admin-layout__header-main">' +
' <h5 class="adv-menu-admin-layout__title">' +
' <i class="icon-th-large"></i>' +
' <span class="adv-menu-admin-layout__title-text">' + escapeHtml(t('block', 'Block')) + ' #' + escapeHtml(String(index + 1)) + '</span>' +
' </h5>' +
' <div class="adv-menu-admin-layout__meta">' + meta.join('') + '</div>' +
' </div>' +
' <div class="adv-menu-admin-layout__actions">' +
actionButton('js-layout-up', 'icon-arrow-up', '') +
actionButton('js-layout-down', 'icon-arrow-down', '') +
actionButton('js-layout-edit', 'icon-pencil', t('edit', 'Edit')) +
actionButton('js-layout-delete btn-danger', 'icon-trash', t('delete', 'Delete')) +
' </div>' +
'</div>' +
'<div class="adv-menu-admin-layout__body">' +
(layout.custom_image_url ? '<img src="' + escapeHtml(layout.custom_image_url) + '" alt="" class="adv-menu-image-preview">' : '') +
'</div>';
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 '' +
'<button type="button" class="btn btn-default btn-sm adv-menu-admin-action ' + className + '">' +
' <i class="' + icon + '"></i>' +
(label ? '<span>' + escapeHtml(label) + '</span>' : '') +
'</button>';
}
function metaItem(text, icon) {
return '<span class="adv-menu-admin-node__meta-item"><i class="' + icon + '"></i><span>' + escapeHtml(text) + '</span></span>';
}
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) {
uploadImage(event.target.files[0], 'menu_icon', function (response) {
node.icon_path = response.path;
node.icon_url = response.url;
renderImagePreview(previewWrap, response.url);
serializeTree();
});
};
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 = '' +
'<h5>' + escapeHtml(language.iso_code + ' / ' + language.name) + '</h5>' +
'<div class="adv-megamenu-admin__lang-grid">' +
' <label class="form-group">' +
' <span>' + escapeHtml(t('title', 'Title')) + '</span>' +
' <input type="text" class="form-control js-lang-title" value="' + escapeAttribute(lang.title || '') + '">' +
' </label>' +
(depth === 0 ? '' : (
' <label class="form-group">' +
' <span>' + escapeHtml(t('description', 'Description')) + '</span>' +
' <textarea class="form-control js-lang-description" rows="3">' + escapeHtml(lang.description || '') + '</textarea>' +
' </label>'
)) +
' <label class="form-group">' +
' <span>' + escapeHtml(t('customLinkLabel', 'Custom link')) + '</span>' +
' <input type="text" class="form-control js-lang-link" value="' + escapeAttribute(lang.custom_link || '') + '">' +
' </label>' +
'</div>';
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&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 = '<span class="help-block">' + escapeHtml(t('noProducts', 'No products attached.')) + '</span>';
return;
}
products.forEach(function (productId, index) {
var chip = document.createElement('span');
chip.className = 'adv-menu-product-chip';
chip.innerHTML = '<span>#' + escapeHtml(String(productId)) + '</span><button type="button">&times;</button>';
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) {
var defaultLang = languages[0] ? String(languages[0].id_lang) : null;
if (defaultLang && node.lang[defaultLang] && node.lang[defaultLang].title) {
return node.lang[defaultLang].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) {
var defaultLang = languages[0] ? String(languages[0].id_lang) : null;
if (defaultLang && node.lang[defaultLang] && node.lang[defaultLang].custom_link) {
return node.lang[defaultLang].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) {
if (!file) {
return;
}
var formData = new FormData();
formData.append('ajax', '1');
formData.append('action', 'uploadImage');
formData.append('token', token);
formData.append('preset', preset || 'default');
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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
function escapeAttribute(value) {
return escapeHtml(value).replace(/\n/g, '&#10;');
}
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();
}
})();