1/** 2 * Main 3 */ 4 5'use strict'; 6 7let isRtl = window.Helpers.isRtl(), 8 isDarkStyle = window.Helpers.isDarkStyle(), 9 menu, 10 animate, 11 isHorizontalLayout = false; 12 13if (document.getElementById('layout-menu')) { 14 isHorizontalLayout = document.getElementById('layout-menu').classList.contains('menu-horizontal'); 15} 16 17(function () { 18 // Initialize menu 19 //----------------- 20 21 let layoutMenuEl = document.querySelectorAll('#layout-menu'); 22 layoutMenuEl.forEach(function (element) { 23 menu = new Menu(element, { 24 orientation: isHorizontalLayout ? 'horizontal' : 'vertical', 25 closeChildren: isHorizontalLayout ? true : false, 26 // ? This option only works with Horizontal menu 27 showDropdownOnHover: localStorage.getItem('templateCustomizer-' + templateName + '--ShowDropdownOnHover') // If value(showDropdownOnHover) is set in local storage 28 ? localStorage.getItem('templateCustomizer-' + templateName + '--ShowDropdownOnHover') === 'true' // Use the local storage value 29 : window.templateCustomizer !== undefined // If value is set in config.js 30 ? window.templateCustomizer.settings.defaultShowDropdownOnHover // Use the config.js value 31 : true // Use this if you are not using the config.js and want to set value directly from here 32 }); 33 // Change parameter to true if you want scroll animation 34 window.Helpers.scrollToActive((animate = false)); 35 window.Helpers.mainMenu = menu; 36 }); 37 38 // Initialize menu togglers and bind click on each 39 let menuToggler = document.querySelectorAll('.layout-menu-toggle'); 40 menuToggler.forEach(item => { 41 item.addEventListener('click', event => { 42 event.preventDefault(); 43 window.Helpers.toggleCollapsed(); 44 // Enable menu state with local storage support if enableMenuLocalStorage = true from config.js 45 if (config.enableMenuLocalStorage && !window.Helpers.isSmallScreen()) { 46 try { 47 localStorage.setItem( 48 'templateCustomizer-' + templateName + '--LayoutCollapsed', 49 String(window.Helpers.isCollapsed()) 50 ); 51 } catch (e) {} 52 } 53 }); 54 }); 55 56 // Menu swipe gesture 57 58 // Detect swipe gesture on the target element and call swipe In 59 window.Helpers.swipeIn('.drag-target', function (e) { 60 window.Helpers.setCollapsed(false); 61 }); 62 63 // Detect swipe gesture on the target element and call swipe Out 64 window.Helpers.swipeOut('#layout-menu', function (e) { 65 if (window.Helpers.isSmallScreen()) window.Helpers.setCollapsed(true); 66 }); 67 68 // Display in main menu when menu scrolls 69 let menuInnerContainer = document.getElementsByClassName('menu-inner'), 70 menuInnerShadow = document.getElementsByClassName('menu-inner-shadow')[0]; 71 if (menuInnerContainer.length > 0 && menuInnerShadow) { 72 menuInnerContainer[0].addEventListener('ps-scroll-y', function () { 73 if (this.querySelector('.ps__thumb-y').offsetTop) { 74 menuInnerShadow.style.display = 'block'; 75 } else { 76 menuInnerShadow.style.display = 'none'; 77 } 78 }); 79 } 80 81 // Style Switcher (Light/Dark Mode) 82 //--------------------------------- 83 84 let styleSwitcherToggleEl = document.querySelector('.style-switcher-toggle'); 85 if (window.templateCustomizer) { 86 // setStyle light/dark on click of styleSwitcherToggleEl 87 if (styleSwitcherToggleEl) { 88 styleSwitcherToggleEl.addEventListener('click', function () { 89 if (window.Helpers.isLightStyle()) { 90 window.templateCustomizer.setStyle('dark'); 91 } else { 92 window.templateCustomizer.setStyle('light'); 93 } 94 }); 95 } 96 // Update style switcher icon and tooltip based on current style 97 if (window.Helpers.isLightStyle()) { 98 if (styleSwitcherToggleEl) { 99 styleSwitcherToggleEl.querySelector('i').classList.add('bx-moon'); 100 new bootstrap.Tooltip(styleSwitcherToggleEl, { 101 title: 'Dark mode', 102 fallbackPlacements: ['bottom'] 103 }); 104 } 105 switchImage('light'); 106 } else { 107 if (styleSwitcherToggleEl) { 108 styleSwitcherToggleEl.querySelector('i').classList.add('bx-sun'); 109 new bootstrap.Tooltip(styleSwitcherToggleEl, { 110 title: 'Light mode', 111 fallbackPlacements: ['bottom'] 112 }); 113 } 114 switchImage('dark'); 115 } 116 } 117 118 // Update light/dark image based on current style 119 function switchImage(style) { 120 const switchImagesList = [].slice.call(document.querySelectorAll('[data-app-' + style + '-img]')); 121 switchImagesList.map(function (imageEl) { 122 const setImage = imageEl.getAttribute('data-app-' + style + '-img'); 123 imageEl.src = assetsPath + 'img/' + setImage; // Using window.assetsPath to get the exact relative path 124 });
125 } 126 127 // Navbar Scroll class 128 //--------------------- 129 function scrollTopFn() { 130 if (document.getElementById('layout-navbar')) { 131 if (document.body.scrollTop > 10 || document.documentElement.scrollTop > 10) { 132 document.getElementById('layout-navbar').classList.add('navbar-elevated'); 133 } else { 134 document.getElementById('layout-navbar').classList.remove('navbar-elevated'); 135 } 136 } 137 } 138 window.onscroll = function () { 139 scrollTopFn(); 140 }; 141 // Internationalization (Language Dropdown) 142 // --------------------------------------- 143 144 if (typeof i18next !== 'undefined' && typeof i18NextHttpBackend !== 'undefined') { 145 i18next 146 .use(i18NextHttpBackend) 147 .init({ 148 lng: 'en', 149 debug: false, 150 fallbackLng: 'en', 151 backend: { 152 loadPath: assetsPath + 'json/locales/{{lng}}.json' 153 }, 154 returnObjects: true 155 }) 156 .then(function (t) { 157 localize(); 158 }); 159 } 160 161 let languageDropdown = document.getElementsByClassName('dropdown-language'); 162 163 if (languageDropdown.length) { 164 let dropdownItems = languageDropdown[0].querySelectorAll('.dropdown-item'); 165 166 for (let i = 0; i < dropdownItems.length; i++) { 167 dropdownItems[i].addEventListener('click', function () { 168 let currentLanguage = this.getAttribute('data-language'), 169 selectedLangFlag = this.querySelector('.fi').getAttribute('class'), 170 startsWith = 'fs-', 171 classes = selectedLangFlag.split(' ').filter(function (v) { 172 return v.lastIndexOf(startsWith, 0) !== 0; 173 }); 174 selectedLangFlag = classes.join(' ').trim() + ' fs-3'; 175 176 for (let sibling of this.parentNode.children) { 177 sibling.classList.remove('selected'); 178 } 179 this.classList.add('selected'); 180 181 languageDropdown[0].querySelector('.dropdown-toggle .fi').className = selectedLangFlag; 182 183 i18next.changeLanguage(currentLanguage, (err, t) => { 184 if (err) return console.log('something went wrong loading', err); 185 localize(); 186 }); 187 }); 188 } 189 } 190 191 function localize() { 192 let i18nList = document.querySelectorAll('[data-i18n]'); 193 // Set the current language in dd 194 let currentLanguageEle = document.querySelector('.dropdown-item[data-language="' + i18next.language + '"]'); 195 196 if (currentLanguageEle) { 197 currentLanguageEle.click(); 198 } 199 200 i18nList.forEach(function (item) { 201 item.innerHTML = i18next.t(item.dataset.i18n); 202 }); 203 } 204 205 // Notification 206 // ------------ 207 const notificationMarkAsReadAll = document.querySelector('.dropdown-notifications-all'); 208 const notificationMarkAsReadList = document.querySelectorAll('.dropdown-notifications-read'); 209 210 // Notification: Mark as all as read 211 if (notificationMarkAsReadAll) { 212 notificationMarkAsReadAll.addEventListener('click', event => { 213 notificationMarkAsReadList.forEach(item => { 214 item.closest('.dropdown-notifications-item').classList.add('marked-as-read'); 215 }); 216 }); 217 } 218 // Notification: Mark as read/unread onclick of dot 219 if (notificationMarkAsReadList) { 220 notificationMarkAsReadList.forEach(item => { 221 item.addEventListener('click', event => { 222 item.closest('.dropdown-notifications-item').classList.toggle('marked-as-read'); 223 }); 224 }); 225 } 226 227 // Notification: Mark as read/unread onclick of dot 228 const notificationArchiveMessageList = document.querySelectorAll('.dropdown-notifications-archive'); 229 notificationArchiveMessageList.forEach(item => { 230 item.addEventListener('click', event => { 231 item.closest('.dropdown-notifications-item').remove(); 232 }); 233 }); 234 235 // Init helpers & misc 236 // -------------------- 237 238 // Init BS Tooltip 239 const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); 240 tooltipTriggerList.map(function (tooltipTriggerEl) { 241 return new bootstrap.Tooltip(tooltipTriggerEl); 242 }); 243 244 // Accordion active class 245 const accordionActiveFunction = function (e) { 246 if (e.type == 'show.bs.collapse' || e.type == 'show.bs.collapse') { 247 e.target.closest('.accordion-item').classList.add('active'); 248 } else { 249 e.target.closest('.accordion-item').classList.remove('active'); 250 } 251 }; 252 253 const accordionTriggerList = [].slice.call(document.querySelectorAll('.accordion')); 254 const accordionList = accordionTriggerList.map(function (accordionTriggerEl) { 255 accordionTriggerEl.addEventListener('show.bs.collapse', accordionActiveFunction); 256 accordionTriggerEl.addEventListener('hide.bs.collapse', accordionActiveFunction); 257 }); 258 259 // If layout is RTL add .dropdown-menu-end class to .dropdown-menu 260 if (isRtl) { 261 Helpers._addClass('dropdown-menu-end', document.querySelectorAll('#layout-navbar .dropdown-menu')); 262 } 263 264 // Auto update layout based on screen size 265 window.Helpers.setAutoUpdate(true); 266 267 // Toggle Password Visibility
268 window.Helpers.initPasswordToggle(); 269 270 // Speech To Text 271 window.Helpers.initSpeechToText(); 272 273 // Init PerfectScrollbar in Navbar Dropdown (i.e notification) 274 window.Helpers.initNavbarDropdownScrollbar(); 275 276 // On window resize listener 277 // ------------------------- 278 window.addEventListener( 279 'resize', 280 function (event) { 281 // Hide open search input and set value blank 282 if (window.innerWidth >= window.Helpers.LAYOUT_BREAKPOINT) { 283 if (document.querySelector('.search-input-wrapper')) { 284 document.querySelector('.search-input-wrapper').classList.add('d-none'); 285 document.querySelector('.search-input').value = ''; 286 } 287 } 288 // Horizontal Layout : Update menu based on window size 289 let horizontalMenuTemplate = document.querySelector("[data-template^='horizontal-menu']"); 290 if (horizontalMenuTemplate) { 291 setTimeout(function () { 292 if (window.innerWidth < window.Helpers.LAYOUT_BREAKPOINT) { 293 if (document.getElementById('layout-menu')) { 294 if (document.getElementById('layout-menu').classList.contains('menu-horizontal')) { 295 menu.switchMenu('vertical'); 296 } 297 } 298 } else { 299 if (document.getElementById('layout-menu')) { 300 if (document.getElementById('layout-menu').classList.contains('menu-vertical')) { 301 menu.switchMenu('horizontal'); 302 } 303 } 304 } 305 }, 100); 306 } 307 }, 308 true 309 ); 310 311 // Manage menu expanded/collapsed with templateCustomizer & local storage 312 //------------------------------------------------------------------ 313 314 // If current layout is horizontal OR current window screen is small (overlay menu) than return from here 315 if (isHorizontalLayout || window.Helpers.isSmallScreen()) { 316 return; 317 } 318 319 // If current layout is vertical and current window screen is > small 320 321 // Auto update menu collapsed/expanded based on the themeConfig 322 if (typeof TemplateCustomizer !== 'undefined') { 323 if (window.templateCustomizer.settings.defaultMenuCollapsed) { 324 window.Helpers.setCollapsed(true, false); 325 } 326 } 327 328 // Manage menu expanded/collapsed state with local storage support If enableMenuLocalStorage = true in config.js 329 if (typeof config !== 'undefined') { 330 if (config.enableMenuLocalStorage) { 331 try { 332 if ( 333 localStorage.getItem('templateCustomizer-' + templateName + '--LayoutCollapsed') !== null && 334 localStorage.getItem('templateCustomizer-' + templateName + '--LayoutCollapsed') !== 'false' 335 ) 336 window.Helpers.setCollapsed( 337 localStorage.getItem('templateCustomizer-' + templateName + '--LayoutCollapsed') === 'true', 338 false 339 ); 340 } catch (e) {} 341 } 342 } 343})(); 344 345// ! Removed following code if you do't wish to use jQuery. Remember that navbar search functionality will stop working on removal. 346if (typeof $ !== 'undefined') { 347 $(function () { 348 // ! TODO: Required to load after DOM is ready, did this now with jQuery ready. 349 window.Helpers.initSidebarToggle(); 350 // Toggle Universal Sidebar 351 352 // Navbar Search with autosuggest (typeahead) 353 // ? You can remove the following JS if you don't want to use search functionality. 354 //---------------------------------------------------------------------------------- 355 356 var searchToggler = $('.search-toggler'), 357 searchInputWrapper = $('.search-input-wrapper'), 358 searchInput = $('.search-input'), 359 contentBackdrop = $('.content-backdrop'); 360 361 // Open search input on click of search icon 362 if (searchToggler.length) { 363 searchToggler.on('click', function () { 364 if (searchInputWrapper.length) { 365 searchInputWrapper.toggleClass('d-none'); 366 searchInput.focus(); 367 } 368 }); 369 } 370 // Open search on 'CTRL+/' 371 $(document).on('keydown', function (event) { 372 let ctrlKey = event.ctrlKey, 373 slashKey = event.which === 191; 374 375 if (ctrlKey && slashKey) { 376 if (searchInputWrapper.length) { 377 searchInputWrapper.toggleClass('d-none'); 378 searchInput.focus(); 379 } 380 } 381 });
382 // Todo: Add container-xxl to twitter-typeahead 383 searchInput.on('focus', function () { 384 if (searchInputWrapper.hasClass('container-xxl')) { 385 searchInputWrapper.find('.twitter-typeahead').addClass('container-xxl'); 386 } 387 }); 388 389 if (searchInput.length) { 390 // Filter config 391 var filterConfig = function (data) { 392 return function findMatches(q, cb) { 393 let matches; 394 matches = []; 395 data.filter(function (i) { 396 if (i.name.toLowerCase().startsWith(q.toLowerCase())) { 397 matches.push(i); 398 } else if ( 399 !i.name.toLowerCase().startsWith(q.toLowerCase()) && 400 i.name.toLowerCase().includes(q.toLowerCase()) 401 ) { 402 matches.push(i); 403 matches.sort(function (a, b) { 404 return b.name < a.name ? 1 : -1; 405 }); 406 } else { 407 return []; 408 } 409 }); 410 cb(matches); 411 }; 412 }; 413 414 // Search JSON 415 var searchJson = 'search-vertical.json'; // For vertical layout 416 if ($('#layout-menu').hasClass('menu-horizontal')) { 417 var searchJson = 'search-horizontal.json'; // For vertical layout 418 } 419 // Search API AJAX call 420 var searchData = $.ajax({ 421 url: assetsPath + 'json/' + searchJson, //? Use your own search api instead 422 dataType: 'json', 423 async: false 424 }).responseJSON; 425 // Init typeahead on searchInput 426 searchInput.each(function () { 427 var $this = $(this); 428 searchInput 429 .typeahead( 430 { 431 hint: false, 432 classNames: { 433 menu: 'tt-menu navbar-search-suggestion', 434 cursor: 'active', 435 suggestion: 'suggestion d-flex justify-content-between px-3 py-2 w-100' 436 } 437 }, 438 // ? Add/Update blocks as per need 439 // Pages 440 { 441 name: 'pages', 442 display: 'name', 443 limit: 5, 444 source: filterConfig(searchData.pages), 445 templates: { 446 header: '<h6 class="suggestions-header text-primary mb-0 mx-3 mt-3 pb-2">Pages</h6>', 447 suggestion: function ({ url, icon, name }) { 448 return ( 449 '<a href="' + 450 url + 451 '">' + 452 '<div>' + 453 '<i class="bx ' + 454 icon + 455 ' me-2"></i>' + 456 '<span class="align-middle">' + 457 name + 458 '</span>' + 459 '</div>' + 460 '</a>' 461 ); 462 }, 463 notFound: 464 '<div class="not-found px-3 py-2">' + 465 '<h6 class="suggestions-header text-primary mb-2">Pages</h6>' + 466 '<p class="py-2 mb-0"><i class="bx bx-error-circle bx-xs me-2"></i> No Results Found</p>' + 467 '</div>' 468 } 469 }, 470 // Files 471 { 472 name: 'files', 473 display: 'name', 474 limit: 4, 475 source: filterConfig(searchData.files), 476 templates: { 477 header: '<h6 class="suggestions-header text-primary mb-0 mx-3 mt-3 pb-2">Files</h6>', 478 suggestion: function ({ src, name, subtitle, meta }) { 479 return ( 480 '<a href="javascript:;">' + 481 '<div class="d-flex w-50">' + 482 '<img class="me-3" src="' + 483 assetsPath + 484 src + 485 '" alt="' + 486 name + 487 '" height="32">' + 488 '<div class="w-75">' + 489 '<h6 class="mb-0">' + 490 name + 491 '</h6>' + 492 '<small class="text-muted">' + 493 subtitle + 494 '</small>' + 495 '</div>' + 496 '</div>' + 497 '<small class="text-muted">' + 498 meta + 499 '</small>' + 500 '</a>' 501 ); 502 }, 503 notFound: 504 '<div class="not-found px-3 py-2">' + 505 '<h6 class="suggestions-header text-primary mb-2">Files</h6>' + 506 '<p class="py-2 mb-0"><i class="bx bx-error-circle bx-xs me-2"></i> No Results Found</p>' + 507 '</div>' 508 } 509 }, 510 // Members 511 { 512 name: 'members', 513 display: 'name', 514 limit: 4, 515 source: filterConfig(searchData.members), 516 templates: { 517 header: '<h6 class="suggestions-header text-primary mb-0 mx-3 mt-3 pb-2">Members</h6>', 518 suggestion: function ({ name, src, subtitle }) { 519 return ( 520 '<a href="app-user-view-account.html">' + 521 '<div class="d-flex align-items-center">' + 522 '<img class="rounded-circle me-3" src="' + 523 assetsPath + 524 src + 525 '" alt="' + 526 name + 527 '" height="32">' + 528 '<div class="user-info">' + 529 '<h6 class="mb-0">' + 530 name + 531 '</h6>' + 532 '<small class="text-muted">' + 533 subtitle + 534 '</small>' + 535 '</div>' + 536 '</div>' + 537 '</a>' 538 ); 539 }, 540 notFound: 541 '<div class="not-found px-3 py-2">' + 542 '<h6 class="suggestions-header text-primary mb-2">Members</h6>' + 543 '<p class="py-2 mb-0"><i class="bx bx-error-circle bx-xs me-2"></i> No Results Found</p>' + 544 '</div>' 545 } 546 } 547 ) 548 //On typeahead result render. 549 .bind('typeahead:render', function () { 550 // Show content backdrop, 551 contentBackdrop.addClass('show').removeClass('fade'); 552 }) 553 // On typeahead select 554 .bind('typeahead:select', function (ev, suggestion) { 555 // Open selected page 556 if (suggestion.url) { 557 window.location = suggestion.url; 558 } 559 }) 560 // On typeahead close 561 .bind('typeahead:close', function () { 562 // Clear search 563 searchInput.val(''); 564 $this.typeahead('val', ''); 565 // Hide search input wrapper 566 searchInputWrapper.addClass('d-none'); 567 // Fade content backdrop 568 contentBackdrop.addClass('fade').removeClass('show'); 569 }); 570 571 // On searchInput keyup, Fade content backdrop if search input is blank 572 searchInput.on('keyup', function () { 573 if (searchInput.val() == '') { 574 contentBackdrop.addClass('fade').removeClass('show'); 575 } 576 }); 577 }); 578 579 // Init PerfectScrollbar in search result 580 var psSearch; 581 $('.navbar-search-suggestion').each(function () { 582 psSearch = new PerfectScrollbar($(this)[0], { 583 wheelPropagation: false, 584 suppressScrollX: true 585 }); 586 }); 587 588 searchInput.on('keyup', function () { 589 psSearch.update(); 590 }); 591 } 592 }); 593}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.