PHP Classes

File: themes/default/assets/local/js/scripts.min.js

Recommend this page to a friend!
  Packages of Aby Dahana   Aksara   themes/default/assets/local/js/scripts.min.js   Download  
File: themes/default/assets/local/js/scripts.min.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Aksara
A CodeIgniter based API and CRUD generator
Author: By
Last change: refactor: improve image rendering performance with lazy loading and decoding attributes and update form input identifiers
refactor(lighthouse): update semantic HTML tags, fix data attributes, and improve accessibility across view templates and components
Date: Yesterday
Size: 22,083 bytes
 

Contents

Class file image Download
/** * A script library to import the javascript / css file on the fly * * @author Aby Dahana <abydahana@gmail.com> * @copyright (c) Aksara Laboratory <https://aksaracms.com> * @license MIT License * * This source file is subject to the MIT license that is bundled with this * source code in the LICENSE.txt file. */ "use strict"; var prevScrollpos = window.pageYOffset; /** * Hide navbar on scroll down, restore on scroll up. */ window.addEventListener('scroll', function () { var currentScrollPos = window.pageYOffset, navbar = document.getElementById('header-wrapper'); if (navbar && window.navigator.userAgent.indexOf('Trident/') === -1) { if (navbar.getAttribute('data-hide-on-scroll') !== 'true') { navbar.style.transform = 'translateY(0)'; prevScrollpos = currentScrollPos; return; } navbar.style.transition = 'transform 0.25s ease'; if (prevScrollpos < currentScrollPos && currentScrollPos > navbar.offsetHeight) { navbar.style.transform = 'translateY(-100%)'; } else { navbar.style.transform = 'translateY(0)'; } prevScrollpos = currentScrollPos; } }, { passive: true }); $(document).ready(function() { $('.carousel').on('slide.bs.carousel', function (e) { var nextHeight = $(e.relatedTarget).height(); $(this).find('.active.carousel-item').parent().animate({ height: nextHeight }, 500); }); if ($('[data-role=announcements]').length) { $('body').css({ paddingBottom: 40 }) } }); $(document).ready(function() { // Intersection Observer for scroll fade-in animation if ('IntersectionObserver' in window) { var fadeObserver = new IntersectionObserver(function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); } }); }, { root: null, rootMargin: '0px', threshold: 0.15 }); var observeFadeElements = function() { $('.fade-in:not(.visible)').each(function() { fadeObserver.observe(this); }); }; // Initial run observeFadeElements(); // Watch for dynamically added DOM elements (AJAX) var mutationObserver = new MutationObserver(function(mutations) { var shouldRecheck = false; mutations.forEach(function(mutation) { if (mutation.addedNodes && mutation.addedNodes.length > 0) { shouldRecheck = true; } }); if (shouldRecheck) { observeFadeElements(); } }); mutationObserver.observe(document.body, { childList: true, subtree: true }); } else { // Fallback for older browsers $('.fade-in').addClass('visible'); } }); /* ?????????????????????????????????????????????? Bottom Sheet Drag-to-Close Shared by theme navigation and page sheets ?????????????????????????????????????????????? */ (function () { function initBottomSheetDrag(sheetEl) { if (!sheetEl || sheetEl.dataset.dragToCloseReady === '1') return; var header = sheetEl.querySelector('.offcanvas-header'); if (!header) return; var startY = 0; var startTime = 0; var isDragging = false; function resetDrag() { isDragging = false; sheetEl.classList.remove('is-dragging'); sheetEl.style.removeProperty('--sheet-drag-y'); } function closeSheet() { sheetEl.style.setProperty('--sheet-drag-y', '100vh'); setTimeout(function () { var inst = bootstrap.Offcanvas.getInstance(sheetEl); if (inst) inst.hide(); }, 300); } header.addEventListener('touchstart', function (e) { if (!e.touches || !e.touches.length) return; startY = e.touches[0].clientY; startTime = Date.now(); isDragging = true; sheetEl.classList.add('is-dragging'); }, { passive: true }); header.addEventListener('touchmove', function (e) { if (!isDragging || !e.touches || !e.touches.length) return; var dy = e.touches[0].clientY - startY; if (dy < 0) dy = 0; sheetEl.style.setProperty('--sheet-drag-y', dy + 'px'); }, { passive: true }); function onEnd() { if (!isDragging) return; var dy = parseFloat(sheetEl.style.getPropertyValue('--sheet-drag-y')) || 0; var dt = Math.max(1, Date.now() - startTime); var velocity = dy / dt; isDragging = false; sheetEl.classList.remove('is-dragging'); if (velocity > 0.4 || dy > sheetEl.offsetHeight * 0.35) { closeSheet(); } else { sheetEl.style.setProperty('--sheet-drag-y', '0px'); setTimeout(function () { sheetEl.style.removeProperty('--sheet-drag-y'); }, 300); } } header.addEventListener('touchend', onEnd); header.addEventListener('touchcancel', onEnd); sheetEl.addEventListener('hidden.bs.offcanvas', resetDrag); sheetEl.dataset.dragToCloseReady = '1'; } function initAllBottomSheetDrag(root) { var scope = root || document; if (scope.matches && scope.matches('.offcanvas.offcanvas-bottom')) { initBottomSheetDrag(scope); } scope.querySelectorAll('.offcanvas.offcanvas-bottom').forEach(initBottomSheetDrag); } window.initBottomSheetDrag = initBottomSheetDrag; window.initAllBottomSheetDrag = initAllBottomSheetDrag; document.addEventListener('DOMContentLoaded', function () { initAllBottomSheetDrag(document); }); document.addEventListener('show.bs.offcanvas', function (e) { initBottomSheetDrag(e.target); }); })(); /* ?????????????????????????????????????????????? Bottom Sheet Sub-Menu System Handles unlimited nesting depth via stacked sheets ?????????????????????????????????????????????? */ $(document).ready(function () { var isMobile = function () { return window.innerWidth < 992; }; /* Track open sub-sheets as a stack */ var subSheetStack = []; /** * Build sub-sheet items from a Bootstrap dropdown-menu <ul> * Returns an array of { html, icon, label, href, children (jQuery <ul>) } */ function parseDropdownItems($ul) { var items = []; $ul.children('li').each(function () { var $li = $(this); var $a = $li.children('a').first(); var $hr = $li.children('hr'); if ($hr.length) { items.push({ type: 'divider' }); return; } if (!$a.length) return; var icon = ''; var $icon = $a.children('i.mdi'); if ($icon.length) { icon = $icon[0].outerHTML; } var label = ''; var $span = $a.children('span.hide-on-collapse'); if ($span.length) { label = $span.text().trim(); } else { $span = $a.children('span'); if ($span.length) { label = $span.text().trim(); } else { // Fallback: text content minus icon label = $a.clone().children('i').remove().end().text().trim(); } } var href = $a.attr('href') || '#'; var $childUl = $li.children('ul.dropdown-menu'); var hasChildren = $childUl.length > 0; var extraClass = $a.attr('class') || ''; // Preserve specific class flags var isNoAjax = extraClass.indexOf('no-ajax') !== -1; var isXhr = extraClass.indexOf('--xhr') !== -1; var isDanger = extraClass.indexOf('text-danger') !== -1; items.push({ type: 'link', icon: icon, label: label, href: href, hasChildren: hasChildren, $childUl: hasChildren ? $childUl : null, isNoAjax: isNoAjax, isXhr: isXhr, isDanger: isDanger }); }); return items; } /** * Create and show a sub-sheet */ function openSubSheet(title, items) { /* Backdrop (only one, shared) */ var $backdrop = $('.sub-sheet-backdrop'); if (!$backdrop.length) { $backdrop = $('<div class="sub-sheet-backdrop"></div>').appendTo('body'); } /* Build list HTML */ var listHtml = '<ul class="sub-sheet-list">'; items.forEach(function (item) { if (item.type === 'divider') { listHtml += '<li><hr style="border-color:var(--bs-sheet-divider);margin:4px 16px"></li>'; return; } var cls = []; if (item.isNoAjax) cls.push('no-ajax'); if (item.isXhr) cls.push('--xhr'); if (item.isDanger) cls.push('text-danger'); listHtml += '<li>'; listHtml += '<a href="' + item.href + '"'; if (cls.length) listHtml += ' class="' + cls.join(' ') + '"'; if (item.hasChildren) listHtml += ' data-bs-submenu="true"'; listHtml += '>'; listHtml += item.icon; listHtml += '<span>' + item.label + '</span>'; listHtml += '</a>'; listHtml += '</li>'; }); listHtml += '</ul>'; /* Panel */ var $sheet = $([ '<div class="bottom-sub-sheet">', '<div class="sub-sheet-header" style="position:relative">', '<div class="drag-handle"></div>', '<button type="button" class="sub-sheet-back" aria-label="Back">&lsaquo;</button>', '<span class="sub-sheet-title">' + title + '</span>', '</div>', '<div class="sub-sheet-body">', listHtml, '</div>', '</div>' ].join('')); $sheet.appendTo('body'); /* Keep a reference to items for child navigation */ $sheet.data('items', items); /* Push to stack and assign z-index based on open order */ subSheetStack.push($sheet); var sheetZ = 1070 + (subSheetStack.length * 2); $sheet.css('z-index', sheetZ); updateBackdropZ(); /* Show with rAF for transition */ requestAnimationFrame(function () { $backdrop.addClass('show'); $sheet.addClass('show'); $('body').addClass('bottom-sheet-open'); }); /* Bind events inside this sheet */ $sheet.find('[data-bs-submenu]').on('click', function (e) { e.preventDefault(); e.stopPropagation(); var idx = $(this).closest('li').index(); // Find dividers before this index to adjust var realIdx = 0, count = 0; $sheet.find('.sub-sheet-list > li').each(function(i) { if (i === idx) { return false; // break } if (!$(this).find('hr').length) { count++; } }); // Match with items array (skip dividers) var itemIndex = 0, nonDividerCount = 0; for (var i = 0; i < items.length; i++) { if (items[i].type === 'divider') continue; if (nonDividerCount === count) { itemIndex = i; break; } nonDividerCount++; } var item = items[itemIndex]; if (item && item.hasChildren && item.$childUl) { var childItems = parseDropdownItems(item.$childUl); openSubSheet(item.label, childItems); } }); /* Back button */ $sheet.find('.sub-sheet-back').on('click', function () { closeTopSubSheet(); }); /* Backdrop tap closes all */ $backdrop.off('click.subsheet').on('click.subsheet', function () { closeAllSubSheets(); }); } /** * Close the top-most sub-sheet */ function closeTopSubSheet() { if (!subSheetStack.length) return; var $sheet = subSheetStack.pop(); $sheet.removeClass('show'); setTimeout(function () { $sheet.remove(); }, 380); if (subSheetStack.length === 0) { $('.sub-sheet-backdrop').removeClass('show'); $('body').removeClass('bottom-sheet-open'); } else { updateBackdropZ(); } } /** * Close all sub-sheets */ function closeAllSubSheets() { subSheetStack.forEach(function ($s) { $s.removeClass('show'); setTimeout(function () { $s.remove(); }, 380); }); subSheetStack = []; $('.sub-sheet-backdrop').removeClass('show'); $('body').removeClass('bottom-sheet-open'); } /** * Keep backdrop z-index just below the top-most sheet */ function updateBackdropZ() { if (subSheetStack.length) { var topSheetZ = parseInt(subSheetStack[subSheetStack.length - 1].css('z-index'), 10) || 1058; $('.sub-sheet-backdrop').css('z-index', topSheetZ - 1); } else { $('.sub-sheet-backdrop').css('z-index', 1055); } } /** * Create and show a sub-sheet with AJAX-loaded content */ function openAjaxSubSheet(title, url) { /* Backdrop */ var $backdrop = $('.sub-sheet-backdrop'); if (!$backdrop.length) { $backdrop = $('<div class="sub-sheet-backdrop"></div>').appendTo('body'); } /* Panel with loading indicator */ var $sheet = $([ '<div class="bottom-sub-sheet">', '<div class="sub-sheet-header" style="position:relative">', '<div class="drag-handle"></div>', '<button type="button" class="sub-sheet-back" aria-label="Back">&lsaquo;</button>', '<span class="sub-sheet-title">' + title + '</span>', '</div>', '<div class="sub-sheet-body" style="padding:8px 0">', '<div style="text-align:center;padding:32px 16px;color:var(--bs-sheet-text-muted)">', '<i class="mdi mdi-loading mdi-spin" style="font-size:1.5rem"></i>', '</div>', '</div>', '</div>' ].join('')); $sheet.appendTo('body'); /* Push to stack and assign z-index */ subSheetStack.push($sheet); var sheetZ = 1070 + (subSheetStack.length * 2); $sheet.css('z-index', sheetZ); updateBackdropZ(); /* Show */ requestAnimationFrame(function () { $backdrop.addClass('show'); $sheet.addClass('show'); $('body').addClass('bottom-sheet-open'); }); /* Load content via AJAX (POST with prefer=dropdown) */ $.ajax({ url: url, type: 'POST', data: { prefer: 'dropdown' }, dataType: 'json', success: function (data) { /* JSON response ? render based on structure */ var listHtml = '<ul class="sub-sheet-list">'; if (Array.isArray(data) && data.length) { data.forEach(function (item) { if (item.language && item.code) { /* Language item */ var href = config.base_url + 'xhr/language/' + item.code; listHtml += '<li>'; listHtml += '<a href="' + href + '" class="--xhr">'; listHtml += '<i class="mdi mdi-translate" style="font-size:1.25rem;width:1.5rem;text-align:center;color:var(--bs-sheet-text-muted);flex-shrink:0"></i>'; listHtml += '<span>' + item.language + '</span>'; listHtml += '</a>'; listHtml += '</li>'; } else if (item.user && item.text) { /* Notification item (from Notifications::partials) */ var nHref = item.url || '#'; listHtml += '<li>'; listHtml += '<a href="' + nHref + '" style="gap:10px">'; listHtml += '<img src="' + (item.avatar || '') + '" style="width:36px;height:36px;border-radius:50%;object-fit:cover;flex-shrink:0" alt="' + (item.user || '') + '" loading="lazy" decoding="async" onerror="this.style.display=\'none\'" />'; listHtml += '<span style="display:flex;flex-direction:column;gap:2px;line-height:1.3">'; listHtml += '<strong style="font-size:0.9rem">' + item.user + '</strong>'; listHtml += '<span style="font-size:0.82rem;color:var(--bs-sheet-text-muted)">' + item.text + '</span>'; if (item.timestamp) { listHtml += '<span style="font-size:0.75rem;color:var(--bs-sheet-accent)">' + item.timestamp + '</span>'; } listHtml += '</span>'; listHtml += '</a>'; listHtml += '</li>'; } }); } if (listHtml === '<ul class="sub-sheet-list">') { /* Empty or unrecognised data */ listHtml += '<li style="text-align:center;padding:24px 16px;color:var(--bs-sheet-text-muted)">'; listHtml += '<i class="mdi mdi-emoticon-neutral-outline" style="font-size:1.5rem;display:block;margin-bottom:4px"></i> No items'; listHtml += '</li>'; } listHtml += '</ul>'; $sheet.find('.sub-sheet-body').html(listHtml); }, error: function (xhr) { /* Might be HTML response ? try parsing */ var html = xhr.responseText || ''; try { var $content = $(html); var itemsHtml = '<ul class="sub-sheet-list">'; var found = false; var $items = $content.find('a, .nav-link, .dropdown-item'); if (!$items.length) $items = $content.filter('a, .nav-link, .dropdown-item'); if ($items.length) { found = true; $items.each(function () { var $el = $(this); var icon = ''; var $icon = $el.find('i.mdi').first(); if ($icon.length) icon = $icon[0].outerHTML; var label = $el.clone().children('i').remove().end().text().trim(); var elHref = $el.attr('href') || '#'; var cls = []; if ($el.hasClass('no-ajax')) cls.push('no-ajax'); if ($el.hasClass('--xhr')) cls.push('--xhr'); itemsHtml += '<li>'; itemsHtml += '<a href="' + elHref + '"'; if (cls.length) itemsHtml += ' class="' + cls.join(' ') + '"'; itemsHtml += '>'; itemsHtml += icon; itemsHtml += '<span>' + label + '</span>'; itemsHtml += '</a>'; itemsHtml += '</li>'; }); itemsHtml += '</ul>'; $sheet.find('.sub-sheet-body').html(itemsHtml); } if (!found) { $sheet.find('.sub-sheet-body').html( '<div style="padding:12px 20px">' + html + '</div>' ); } } catch (e) { $sheet.find('.sub-sheet-body').html( '<div style="text-align:center;padding:32px 16px;color:var(--bs-sheet-text-muted)">' + '<i class="mdi mdi-alert-circle-outline" style="font-size:1.5rem"></i>' + '<p style="margin-top:8px">Failed to load content</p>' + '</div>' ); } } }); /* Back button */ $sheet.find('.sub-sheet-back').on('click', function () { closeTopSubSheet(); }); /* Backdrop tap closes all */ $backdrop.off('click.subsheet').on('click.subsheet', function () { closeAllSubSheets(); }); } /** * Initialise: patch dropdown toggles inside offcanvas for mobile */ function initMobileBottomSheet() { if (!isMobile()) return; var $offcanvas = $('#offcanvasNavbarDark'); /* Find all dropdown-toggle links and mark them */ $offcanvas.find('.nav-link.dropdown-toggle').each(function () { var $a = $(this); var $li = $a.closest('li'); var $ul = $li.children('ul.dropdown-menu'); /* Mark as submenu trigger ? whether it has static children or will load via AJAX */ $a.attr('data-bs-submenu', 'true'); /* Store whether this is an AJAX dropdown (empty menu with a valid URL) */ var hasStaticChildren = $ul.length && $ul.children('li').length > 0; var href = $a.attr('href') || ''; var isAjax = !hasStaticChildren && href && href !== '#' && href !== 'javascript:void(0)'; if (isAjax) { $a.attr('data-bs-submenu-ajax', href); } /* Prevent Bootstrap dropdown */ $a.removeAttr('data-bs-toggle'); $a.removeClass('dropdown-toggle'); }); /* Delegate click for submenu triggers */ $offcanvas.off('click.bottomsheet').on('click.bottomsheet', '[data-bs-submenu]', function (e) { e.preventDefault(); e.stopPropagation(); var $a = $(this); var $li = $a.closest('li'); var label = $a.find('span').first().text().trim() || $a.text().trim(); /* Check if this is an AJAX sub-sheet */ var ajaxUrl = $a.attr('data-bs-submenu-ajax'); if (ajaxUrl) { openAjaxSubSheet(label, ajaxUrl); return; } /* Static children sub-sheet */ var $ul = $li.children('ul.dropdown-menu'); if (!$ul.length) return; var items = parseDropdownItems($ul); openSubSheet(label, items); }); } /* Run on offcanvas show */ var offcanvasEl = document.getElementById('offcanvasNavbarDark'); if (offcanvasEl) { var mobileMenuToggle = document.querySelector('.mobile-menu-toggle[data-bs-target="#offcanvasNavbarDark"]'); offcanvasEl.addEventListener('show.bs.offcanvas', function () { if (mobileMenuToggle) { mobileMenuToggle.classList.add('is-open'); mobileMenuToggle.setAttribute('aria-expanded', 'true'); } initMobileBottomSheet(); }); offcanvasEl.addEventListener('hide.bs.offcanvas', function () { if (mobileMenuToggle) { mobileMenuToggle.classList.remove('is-open'); mobileMenuToggle.setAttribute('aria-expanded', 'false'); } }); /* Clean up sub-sheets when offcanvas hides */ offcanvasEl.addEventListener('hidden.bs.offcanvas', function () { if (mobileMenuToggle) { mobileMenuToggle.classList.remove('is-open'); mobileMenuToggle.setAttribute('aria-expanded', 'false'); } closeAllSubSheets(); }); } /* Also init on page load in case offcanvas is already visible */ initMobileBottomSheet(); /* Re-init on resize crossing the breakpoint */ var wasDesktop = !isMobile(); $(window).on('resize', function () { var nowDesktop = !isMobile(); if (wasDesktop && !nowDesktop) { initMobileBottomSheet(); } wasDesktop = nowDesktop; }); });