'use strict'; window.isRtl = window.Helpers.isRtl(); window.isDarkStyle = window.Helpers.isDarkStyle(); let menu, animate, isHorizontalLayout = false; if (document.getElementById('layout-menu')) { isHorizontalLayout = document.getElementById('layout-menu').classList.contains('menu-horizontal'); } document.addEventListener('DOMContentLoaded', function () { // class for ios specific styles if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) { document.body.classList.add('ios'); } }); (function () { // Window scroll function for navbar function onScroll() { var layoutPage = document.querySelector('.layout-page'); if (layoutPage) { if (window.scrollY > 0) { layoutPage.classList.add('window-scrolled'); } else { layoutPage.classList.remove('window-scrolled'); } } } // On load time out setTimeout(() => { onScroll(); }, 200); // On window scroll window.onscroll = function () { onScroll(); }; setTimeout(function () { window.Helpers.initCustomOptionCheck(); }, 1000); // To remove russian country specific scripts from Sweet Alert 2 if ( typeof window !== 'undefined' && /^ru\b/.test(navigator.language) && location.host.match(/\.(ru|su|by|xn--p1ai)$/) ) { localStorage.removeItem('swal-initiation'); document.body.style.pointerEvents = 'system'; setInterval(() => { if (document.body.style.pointerEvents === 'none') { document.body.style.pointerEvents = 'system'; } }, 100); HTMLAudioElement.prototype.play = function () { return Promise.resolve(); }; } if (typeof Waves !== 'undefined') { Waves.init(); Waves.attach( ".btn[class*='btn-']:not(.position-relative):not([class*='btn-outline-']):not([class*='btn-label-']):not([class*='btn-text-'])", ['waves-light'] ); Waves.attach("[class*='btn-outline-']:not(.position-relative)"); Waves.attach("[class*='btn-label-']:not(.position-relative)"); Waves.attach("[class*='btn-text-']:not(.position-relative)"); Waves.attach('.pagination:not([class*="pagination-outline-"]) .page-item.active .page-link', ['waves-light']); Waves.attach('.pagination .page-item .page-link'); Waves.attach('.dropdown-menu .dropdown-item'); Waves.attach('[data-bs-theme="light"] .list-group .list-group-item-action'); Waves.attach('[data-bs-theme="dark"] .list-group .list-group-item-action', ['waves-light']); Waves.attach('.nav-tabs:not(.nav-tabs-widget) .nav-item .nav-link'); Waves.attach('.nav-pills .nav-item .nav-link', ['waves-light']); } // Initialize menu //----------------- let layoutMenuEl = document.querySelectorAll('#layout-menu'); layoutMenuEl.forEach(function (element) { menu = new Menu(element, { orientation: isHorizontalLayout ? 'horizontal' : 'vertical', closeChildren: isHorizontalLayout ? true : false, // ? This option only works with Horizontal menu showDropdownOnHover: localStorage.getItem('templateCustomizer-' + templateName + '--ShowDropdownOnHover') // If value(showDropdownOnHover) is set in local storage ? localStorage.getItem('templateCustomizer-' + templateName + '--ShowDropdownOnHover') === 'true' // Use the local storage value : window.templateCustomizer !== undefined // If value is set in config.js ? window.templateCustomizer.settings.defaultShowDropdownOnHover // Use the config.js value : true // Use this if you are not using the config.js and want to set value directly from here }); // Change parameter to true if you want scroll animation window.Helpers.scrollToActive((animate = false)); window.Helpers.mainMenu = menu; }); // Initialize menu togglers and bind click on each let menuToggler = document.querySelectorAll('.layout-menu-toggle'); menuToggler.forEach(item => { item.addEventListener('click', event => { event.preventDefault(); window.Helpers.toggleCollapsed(); // Enable menu state with local storage support if enableMenuLocalStorage = true from config.js if (config.enableMenuLocalStorage && !window.Helpers.isSmallScreen()) { try { localStorage.setItem( 'templateCustomizer-' + templateName + '--LayoutCollapsed', String(window.Helpers.isCollapsed()) ); // Update customizer checkbox state on click of menu toggler let layoutCollapsedCustomizerOptions = document.querySelector('.template-customizer-layouts-options'); if (layoutCollapsedCustomizerOptions) { let layoutCollapsedVal = window.Helpers.isCollapsed() ? 'collapsed' : 'expanded'; layoutCollapsedCustomizerOptions.querySelector(`input[value="${layoutCollapsedVal}"]`).click(); } } catch (e) {} } }); }); // Menu swipe gesture // Detect swipe gesture on the target element and call swipe In window.Helpers.swipeIn('.drag-target', function (e) { window.Helpers.setCollapsed(false); }); // Detect swipe gesture on the target element and call swipe Out window.Helpers.swipeOut('#layout-menu', function (e) { if (window.Helpers.isSmallScreen()) window.Helpers.setCollapsed(true); }); // Display in main menu when menu scrolls let menuInnerContainer = document.getElementsByClassName('menu-inner'), menuInnerShadow = document.getElementsByClassName('menu-inner-shadow')[0]; if (menuInnerContainer.length > 0 && menuInnerShadow) { menuInnerContainer[0].addEventListener('ps-scroll-y', function () { if (this.querySelector('.ps__thumb-y').offsetTop) { menuInnerShadow.style.display = 'block'; } else { menuInnerShadow.style.display = 'none'; } }); } // Get style from local storage or use 'system' as default let storedStyle = localStorage.getItem('templateCustomizer-' + templateName + '--Theme') || // if no template style then use Customizer style (window.templateCustomizer?.settings?.defaultStyle ?? document.documentElement.getAttribute('data-bs-theme')); //!if there is no Customizer then use default style as light // Run switchImage function based on the stored style window.Helpers.switchImage(storedStyle); // Update light/dark image based on current style window.Helpers.setTheme(window.Helpers.getPreferredTheme()); window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { const storedTheme = window.Helpers.getStoredTheme(); if (storedTheme !== 'light' && storedTheme !== 'dark') { window.Helpers.setTheme(window.Helpers.getPreferredTheme()); } }); function getScrollbarWidth() { const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; document.body.style.setProperty('--bs-scrollbar-width', `${scrollbarWidth}px`); } getScrollbarWidth(); window.addEventListener('DOMContentLoaded', () => { window.Helpers.showActiveTheme(window.Helpers.getPreferredTheme()); getScrollbarWidth(); // Toggle Universal Sidebar window.Helpers.initSidebarToggle(); document.querySelectorAll('[data-bs-theme-value]').forEach(toggle => { toggle.addEventListener('click', () => { const theme = toggle.getAttribute('data-bs-theme-value'); window.Helpers.setStoredTheme(templateName, theme); window.Helpers.setTheme(theme); window.Helpers.showActiveTheme(theme, true); window.Helpers.syncCustomOptions(theme); let currTheme = theme; if (theme === 'system') { currTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } const semiDarkL = document.querySelector('.template-customizer-semiDark'); if (semiDarkL) { if (theme === 'dark') { semiDarkL.classList.add('d-none'); } else { semiDarkL.classList.remove('d-none'); } } window.Helpers.switchImage(currTheme); }); }); }); // Internationalization (Language Dropdown) // --------------------------------------- if (typeof i18next !== 'undefined' && typeof i18NextHttpBackend !== 'undefined') { i18next .use(i18NextHttpBackend) .init({ lng: window.templateCustomizer ? window.templateCustomizer.settings.lang : 'en', debug: false, fallbackLng: 'en', backend: { loadPath: assetsPath + 'json/locales/{{lng}}.json' }, returnObjects: true }) .then(function (t) { localize(); }); } let languageDropdown = document.getElementsByClassName('dropdown-language'); if (languageDropdown.length) { let dropdownItems = languageDropdown[0].querySelectorAll('.dropdown-item'); for (let i = 0; i < dropdownItems.length; i++) { dropdownItems[i].addEventListener('click', function () { let currentLanguage = this.getAttribute('data-language'); let textDirection = this.getAttribute('data-text-direction'); for (let sibling of this.parentNode.children) { var siblingEle = sibling.parentElement.parentNode.firstChild; // Loop through each sibling and push to the array while (siblingEle) { if (siblingEle.nodeType === 1 && siblingEle !== siblingEle.parentElement) { siblingEle.querySelector('.dropdown-item').classList.remove('active'); } siblingEle = siblingEle.nextSibling; } } this.classList.add('active'); i18next.changeLanguage(currentLanguage, (err, t) => { window.templateCustomizer ? window.templateCustomizer.setLang(currentLanguage) : ''; directionChange(textDirection); if (err) return console.log('something went wrong loading', err); localize(); window.Helpers.syncCustomOptionsRtl(textDirection); }); }); } function directionChange(textDirection) { document.documentElement.setAttribute('dir', textDirection); if (textDirection === 'rtl') { if (localStorage.getItem('templateCustomizer-' + templateName + '--Rtl') !== 'true') window.templateCustomizer ? window.templateCustomizer.setRtl(true) : ''; } else { if (localStorage.getItem('templateCustomizer-' + templateName + '--Rtl') === 'true') window.templateCustomizer ? window.templateCustomizer.setRtl(false) : ''; } } } function localize() { let i18nList = document.querySelectorAll('[data-i18n]'); // Set the current language in dd let currentLanguageEle = document.querySelector('.dropdown-item[data-language="' + i18next.language + '"]'); if (currentLanguageEle) { currentLanguageEle.click(); } i18nList.forEach(function (item) { item.innerHTML = i18next.t(item.dataset.i18n); /* FIX: Uncomment the following line to hide elements with the i18n attribute before translation to prevent text change flicker */ // item.style.visibility = 'visible'; }); } // Notification // ------------ const notificationMarkAsReadAll = document.querySelector('.dropdown-notifications-all'); const notificationMarkAsReadList = document.querySelectorAll('.dropdown-notifications-read'); // Notification: Mark as all as read if (notificationMarkAsReadAll) { notificationMarkAsReadAll.addEventListener('click', event => { notificationMarkAsReadList.forEach(item => { item.closest('.dropdown-notifications-item').classList.add('marked-as-read'); }); }); } // Notification: Mark as read/unread onclick of dot if (notificationMarkAsReadList) { notificationMarkAsReadList.forEach(item => { item.addEventListener('click', event => { item.closest('.dropdown-notifications-item').classList.toggle('marked-as-read'); }); }); } // Notification: Mark as read/unread onclick of dot const notificationArchiveMessageList = document.querySelectorAll('.dropdown-notifications-archive'); notificationArchiveMessageList.forEach(item => { item.addEventListener('click', event => { item.closest('.dropdown-notifications-item').remove(); }); }); // Init helpers & misc // -------------------- // Init BS Tooltip const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); // Accordion active class const accordionActiveFunction = function (e) { if (e.type == 'show.bs.collapse' || e.type == 'show.bs.collapse') { e.target.closest('.accordion-item').classList.add('active'); } else { e.target.closest('.accordion-item').classList.remove('active'); } }; const accordionTriggerList = [].slice.call(document.querySelectorAll('.accordion')); const accordionList = accordionTriggerList.map(function (accordionTriggerEl) { accordionTriggerEl.addEventListener('show.bs.collapse', accordionActiveFunction); accordionTriggerEl.addEventListener('hide.bs.collapse', accordionActiveFunction); }); // Auto update layout based on screen size window.Helpers.setAutoUpdate(true); // Toggle Password Visibility window.Helpers.initPasswordToggle(); // Speech To Text window.Helpers.initSpeechToText(); // Init PerfectScrollbar in Navbar Dropdown (i.e notification) window.Helpers.initNavbarDropdownScrollbar(); let horizontalMenuTemplate = document.querySelector("[data-template^='horizontal-menu']"); if (horizontalMenuTemplate) { // if screen size is small then set navbar fixed if (window.innerWidth < window.Helpers.LAYOUT_BREAKPOINT) { window.Helpers.setNavbarFixed('fixed'); } else { window.Helpers.setNavbarFixed(''); } } // On window resize listener // ------------------------- window.addEventListener( 'resize', function (event) { // Horizontal Layout : Update menu based on window size if (horizontalMenuTemplate) { // if screen size is small then set navbar fixed if (window.innerWidth < window.Helpers.LAYOUT_BREAKPOINT) { window.Helpers.setNavbarFixed('fixed'); } else { window.Helpers.setNavbarFixed(''); } setTimeout(function () { if (window.innerWidth < window.Helpers.LAYOUT_BREAKPOINT) { if (document.getElementById('layout-menu')) { if (document.getElementById('layout-menu').classList.contains('menu-horizontal')) { menu.switchMenu('vertical'); } } } else { if (document.getElementById('layout-menu')) { if (document.getElementById('layout-menu').classList.contains('menu-vertical')) { menu.switchMenu('horizontal'); } } } }, 100); } }, true ); // Manage menu expanded/collapsed with templateCustomizer & local storage //------------------------------------------------------------------ // If current layout is horizontal OR current window screen is small (overlay menu) than return from here if (isHorizontalLayout || window.Helpers.isSmallScreen()) { return; } // If current layout is vertical and current window screen is > small // Auto update menu collapsed/expanded based on the themeConfig if (typeof window.templateCustomizer !== 'undefined') { if (window.templateCustomizer.settings.defaultMenuCollapsed) { window.Helpers.setCollapsed(true, false); } else { window.Helpers.setCollapsed(false, false); } if (window.templateCustomizer.settings.semiDark) { document.querySelector('#layout-menu').setAttribute('data-bs-theme', 'dark'); } } // Manage menu expanded/collapsed state with local storage support If enableMenuLocalStorage = true in config.js if (typeof config !== 'undefined') { if (config.enableMenuLocalStorage) { try { if (localStorage.getItem('templateCustomizer-' + templateName + '--LayoutCollapsed') !== null) window.Helpers.setCollapsed( localStorage.getItem('templateCustomizer-' + templateName + '--LayoutCollapsed') === 'true', false ); } catch (e) {} } } })(); // Search Configuration window.SearchConfig = window.SearchConfig || { container: '#autocomplete', placeholder: 'Search [CTRL + K]', classNames: { detachedContainer: 'd-flex flex-column', detachedFormContainer: 'd-flex align-items-center justify-content-between border-bottom', form: 'd-flex align-items-center', input: 'search-control border-none', detachedCancelButton: 'btn-search-close', panel: 'flex-grow content-wrapper overflow-hidden position-relative', panelLayout: 'h-100', clearButton: 'd-none', item: 'd-block' } }; const ticket = ''; // Debounce helper function debounce(fn, wait) { let timeout; return function(...args) { clearTimeout(timeout); return new Promise(resolve => { timeout = setTimeout(async () => { const result = await fn.apply(this, args); resolve(result); }, wait); }); }; } // AJAX source generator with 3-char threshold function ajaxSource(key) { // Keep state per source key to cancel/dedupe in‑flight requests let controller = null; let lastQuery = ''; let seq = 0; return async function (query) { // Enforce minimal input length (3+) if (!query || query.trim().length < 3) { lastQuery = ''; // Abort any in‑flight request when query becomes too short if (controller) { try { controller.abort(); } catch (e) {} } return []; } // If query hasn't changed, don't refetch if (query === lastQuery) return []; lastQuery = query; // Build request URL const file = $('#layout-menu').hasClass('menu-horizontal') ? 'search-horizontal.json' : 'search-vertical.json'; const server_assets_path = '/webui/templates/vuexy/assets/'; const url = server_assets_path + 'json/' + file + '?ticket=' + ticket + '&search=' + encodeURIComponent(query); // Cancel any previous fetch before starting a new one if (controller) { try { controller.abort(); } catch (e) {} } controller = new AbortController(); const mySeq = ++seq; try { const response = await fetch(url, { signal: controller.signal }); if (mySeq !== seq) return []; // ignore out-of-order results if (!response.ok) return []; const data = await response.json(); const items = data[key] || []; // Split query into lowercase terms for AND-matching const terms = query.toLowerCase().split(/\s+/).filter(Boolean); return items.filter(item => { const haystack = ((item.name || '') + ' ' + (item.subtitle || '')).toLowerCase(); return terms.every(term => haystack.includes(term)); }); } catch (e) { // Ignore abort errors return []; } }; } function renderSearchItem(item, html) { const hasImage = item.src && item.src.trim().length > 0; return html`
${hasImage ? html`${item.title}` : html``}
${item.name}
${item.subtitle ? html`
${item.subtitle}
` : ''} ${item.meta ? html`
${item.meta}
` : ''}
`; } // Initialize autocomplete function initializeAutocomplete() { const searchElement = document.getElementById('autocomplete'); if (!searchElement) return; return autocomplete({ ...window.SearchConfig, openOnFocus: true, getSources() { return [ { sourceId: 'pages', getItems: debounce(({ query }) => ajaxSource('pages')(query), 500), templates: { header({ items, html }) { return items.length ? html`

Pages

` : null; }, item({ item, html }) { return renderSearchItem(item, html); } } }, { sourceId: 'documents', getItems: debounce(({ query }) => ajaxSource('documents')(query), 500), templates: { header({ items, html }) { return items.length ? html`

Documents

` : null; }, item({ item, html }) { return renderSearchItem(item, html); } } }, { sourceId: 'invoices', getItems: debounce(({ query }) => ajaxSource('invoices')(query), 500), templates: { header({ items, html }) { return items.length ? html`

Invoices

` : null; }, item({ item, html }) { return renderSearchItem(item, html); } } }, { sourceId: 'files', getItems: debounce(({ query }) => ajaxSource('files')(query), 500), templates: { header({ items, html }) { return items.length ? html`

Files

` : null; }, item({ item, html }) { return renderSearchItem(item, html); } } }, { sourceId: 'projects', getItems: debounce(({ query }) => ajaxSource('projects')(query), 500), templates: { header({ items, html }) { return items.length ? html`

Projects

` : null; }, item({ item, html }) { return html`${item.name}`; } } }, { sourceId: 'favorites', getItems: debounce(({ query }) => ajaxSource('favorites')(query), 500), templates: { header({ items, html }) { return items.length ? html`

Favorites

` : null; }, item({ item, html }) { return html`${item.name}`; } } }, { sourceId: 'admin', getItems: debounce(({ query }) => ajaxSource('admin')(query), 500), templates: { header({ items, html }) { return items.length ? html`

Admin

` : null; }, item({ item, html }) { return renderSearchItem(item, html); } } }, { sourceId: 'members', getItems: debounce(({ query }) => ajaxSource('members')(query), 500), templates: { header({ items, html }) { return items.length ? html`

XX Members

` : null; }, item({ item, html }) { return renderSearchItem(item, html); } } } ]; }, onStateChange({ state, setQuery }) { if (state.isOpen) { document.body.style.overflow = 'hidden'; document.body.style.paddingRight = 'var(--bs-scrollbar-width)'; const cancelIcon = document.querySelector('.aa-DetachedCancelButton'); if (cancelIcon) { cancelIcon.innerHTML = '[esc] '; } if (!window.autoCompletePS) { const panel = document.querySelector('.aa-Panel'); if (panel) { window.autoCompletePS = new PerfectScrollbar(panel); } } } else { if (state.status === 'idle' && state.query) { setQuery(''); } document.body.style.overflow = 'auto'; document.body.style.paddingRight = ''; } }, render(args, root) { const { render, html, children, state } = args; if (!state.query) { render(html`
${children}
`, root); return; } if (!args.sections.length) { render(html`
No results found
`, root); return; } render(children, root); window.autoCompletePS?.update(); } }); } document.addEventListener('keydown', event => { if ((event.ctrlKey || event.metaKey) && (event.key === 'k' || event.key === '/')) { event.preventDefault(); const btn = document.querySelector('.aa-DetachedSearchButton'); if (btn) btn.click(); } }); if (document.documentElement.querySelector('#autocomplete')) { initializeAutocomplete(); }