ok
Direktori : /home1/indocan/public_html/admin/js/ |
Current File : /home1/indocan/public_html/admin/js/helpers.js |
// Constants const TRANS_EVENTS = ['transitionend', 'webkitTransitionEnd', 'oTransitionEnd'] const TRANS_PROPERTIES = ['transition', 'MozTransition', 'webkitTransition', 'WebkitTransition', 'OTransition'] const INLINE_STYLES = ` .layout-menu-fixed .layout-navbar-full .layout-menu, .layout-page { padding-top: {navbarHeight}px !important; } .content-wrapper { padding-bottom: {footerHeight}px !important; }` // Guard function requiredParam(name) { throw new Error(`Parameter required${name ? `: \`${name}\`` : ''}`) } const Helpers = { // Root Element ROOT_EL: typeof window !== 'undefined' ? document.documentElement : null, // Large screens breakpoint LAYOUT_BREAKPOINT: 1200, // Resize delay in milliseconds RESIZE_DELAY: 200, menuPsScroll: null, mainMenu: null, // Internal variables _curStyle: null, _styleEl: null, _resizeTimeout: null, _resizeCallback: null, _transitionCallback: null, _transitionCallbackTimeout: null, _listeners: [], _initialized: false, _autoUpdate: false, _lastWindowHeight: 0, // ******************************************************************************* // * Utilities // --- // Scroll To Active Menu Item _scrollToActive(animate = false, duration = 500) { const layoutMenu = this.getLayoutMenu() if (!layoutMenu) return let activeEl = layoutMenu.querySelector('li.menu-item.active:not(.open)') if (activeEl) { // t = current time // b = start value // c = change in value // d = duration const easeInOutQuad = (t, b, c, d) => { t /= d / 2 if (t < 1) return (c / 2) * t * t + b t -= 1 return (-c / 2) * (t * (t - 2) - 1) + b } const element = this.getLayoutMenu().querySelector('.menu-inner') if (typeof activeEl === 'string') { activeEl = document.querySelector(activeEl) } if (typeof activeEl !== 'number') { activeEl = activeEl.getBoundingClientRect().top + element.scrollTop } // If active element's top position is less than 2/3 (66%) of menu height than do not scroll if (activeEl < parseInt((element.clientHeight * 2) / 3, 10)) return const start = element.scrollTop const change = activeEl - start - parseInt(element.clientHeight / 2, 10) const startDate = +new Date() if (animate === true) { const animateScroll = () => { const currentDate = +new Date() const currentTime = currentDate - startDate const val = easeInOutQuad(currentTime, start, change, duration) element.scrollTop = val if (currentTime < duration) { requestAnimationFrame(animateScroll) } else { element.scrollTop = change } } animateScroll() } else { element.scrollTop = change } } }, // --- // Add classes _addClass(cls, el = this.ROOT_EL) { if (el.length !== undefined) { // Add classes to multiple elements el.forEach(e => { cls.split(' ').forEach(c => e.classList.add(c)) }) } else { // Add classes to single element cls.split(' ').forEach(c => el.classList.add(c)) } }, // --- // Remove classes _removeClass(cls, el = this.ROOT_EL) { if (el.length !== undefined) { // Remove classes to multiple elements el.forEach(e => { cls.split(' ').forEach(c => e.classList.remove(c)) }) } else { // Remove classes to single element cls.split(' ').forEach(c => el.classList.remove(c)) } }, // Toggle classes _toggleClass(el = this.ROOT_EL, cls1, cls2) { if (el.classList.contains(cls1)) { el.classList.replace(cls1, cls2) } else { el.classList.replace(cls2, cls1) } }, // --- // Has class _hasClass(cls, el = this.ROOT_EL) { let result = false cls.split(' ').forEach(c => { if (el.classList.contains(c)) result = true }) return result }, _findParent(el, cls) { if ((el && el.tagName.toUpperCase() === 'BODY') || el.tagName.toUpperCase() === 'HTML') return null el = el.parentNode while (el && el.tagName.toUpperCase() !== 'BODY' && !el.classList.contains(cls)) { el = el.parentNode } el = el && el.tagName.toUpperCase() !== 'BODY' ? el : null return el }, // --- // Trigger window event _triggerWindowEvent(name) { if (typeof window === 'undefined') return if (document.createEvent) { let event if (typeof Event === 'function') { event = new Event(name) } else { event = document.createEvent('Event') event.initEvent(name, false, true) } window.dispatchEvent(event) } else { window.fireEvent(`on${name}`, document.createEventObject()) } }, // --- // Trigger event _triggerEvent(name) { this._triggerWindowEvent(`layout${name}`) this._listeners.filter(listener => listener.event === name).forEach(listener => listener.callback.call(null)) }, // --- // Update style _updateInlineStyle(navbarHeight = 0, footerHeight = 0) { if (!this._styleEl) { this._styleEl = document.createElement('style') this._styleEl.type = 'text/css' document.head.appendChild(this._styleEl) } const newStyle = INLINE_STYLES.replace(/\{navbarHeight\}/gi, navbarHeight).replace( /\{footerHeight\}/gi, footerHeight ) if (this._curStyle !== newStyle) { this._curStyle = newStyle this._styleEl.textContent = newStyle } }, // --- // Remove style _removeInlineStyle() { if (this._styleEl) document.head.removeChild(this._styleEl) this._styleEl = null this._curStyle = null }, // --- // Redraw layout menu (Safari bugfix) _redrawLayoutMenu() { const layoutMenu = this.getLayoutMenu() if (layoutMenu && layoutMenu.querySelector('.menu')) { const inner = layoutMenu.querySelector('.menu-inner') const { scrollTop } = inner const pageScrollTop = document.documentElement.scrollTop layoutMenu.style.display = 'none' // layoutMenu.offsetHeight layoutMenu.style.display = '' inner.scrollTop = scrollTop document.documentElement.scrollTop = pageScrollTop return true } return false }, // --- // Check for transition support _supportsTransitionEnd() { if (window.QUnit) return false const el = document.body || document.documentElement if (!el) return false let result = false TRANS_PROPERTIES.forEach(evnt => { if (typeof el.style[evnt] !== 'undefined') result = true }) return result }, // --- // Calculate current navbar height _getNavbarHeight() { const layoutNavbar = this.getLayoutNavbar() if (!layoutNavbar) return 0 if (!this.isSmallScreen()) return layoutNavbar.getBoundingClientRect().height // Needs some logic to get navbar height on small screens const clonedEl = layoutNavbar.cloneNode(true) clonedEl.id = null clonedEl.style.visibility = 'hidden' clonedEl.style.position = 'absolute' Array.prototype.slice.call(clonedEl.querySelectorAll('.collapse.show')).forEach(el => this._removeClass('show', el)) layoutNavbar.parentNode.insertBefore(clonedEl, layoutNavbar) const navbarHeight = clonedEl.getBoundingClientRect().height clonedEl.parentNode.removeChild(clonedEl) return navbarHeight }, // --- // Get current footer height _getFooterHeight() { const layoutFooter = this.getLayoutFooter() if (!layoutFooter) return 0 return layoutFooter.getBoundingClientRect().height }, // --- // Get animation duration of element _getAnimationDuration(el) { const duration = window.getComputedStyle(el).transitionDuration return parseFloat(duration) * (duration.indexOf('ms') !== -1 ? 1 : 1000) }, // --- // Set menu hover state _setMenuHoverState(hovered) { this[hovered ? '_addClass' : '_removeClass']('layout-menu-hover') }, // --- // Toggle collapsed _setCollapsed(collapsed) { if (this.isSmallScreen()) { if (collapsed) { this._removeClass('layout-menu-expanded') } else { setTimeout( () => { this._addClass('layout-menu-expanded') }, this._redrawLayoutMenu() ? 5 : 0 ) } } }, // --- // Add layout sivenav toggle animationEnd event _bindLayoutAnimationEndEvent(modifier, cb) { const menu = this.getMenu() const duration = menu ? this._getAnimationDuration(menu) + 50 : 0 if (!duration) { modifier.call(this) cb.call(this) return } this._transitionCallback = e => { if (e.target !== menu) return this._unbindLayoutAnimationEndEvent() cb.call(this) } TRANS_EVENTS.forEach(e => { menu.addEventListener(e, this._transitionCallback, false) }) modifier.call(this) this._transitionCallbackTimeout = setTimeout(() => { this._transitionCallback.call(this, { target: menu }) }, duration) }, // --- // Remove layout sivenav toggle animationEnd event _unbindLayoutAnimationEndEvent() { const menu = this.getMenu() if (this._transitionCallbackTimeout) { clearTimeout(this._transitionCallbackTimeout) this._transitionCallbackTimeout = null } if (menu && this._transitionCallback) { TRANS_EVENTS.forEach(e => { menu.removeEventListener(e, this._transitionCallback, false) }) } if (this._transitionCallback) { this._transitionCallback = null } }, // --- // Bind delayed window resize event _bindWindowResizeEvent() { this._unbindWindowResizeEvent() const cb = () => { if (this._resizeTimeout) { clearTimeout(this._resizeTimeout) this._resizeTimeout = null } this._triggerEvent('resize') } this._resizeCallback = () => { if (this._resizeTimeout) clearTimeout(this._resizeTimeout) this._resizeTimeout = setTimeout(cb, this.RESIZE_DELAY) } window.addEventListener('resize', this._resizeCallback, false) }, // --- // Unbind delayed window resize event _unbindWindowResizeEvent() { if (this._resizeTimeout) { clearTimeout(this._resizeTimeout) this._resizeTimeout = null } if (this._resizeCallback) { window.removeEventListener('resize', this._resizeCallback, false) this._resizeCallback = null } }, _bindMenuMouseEvents() { if (this._menuMouseEnter && this._menuMouseLeave && this._windowTouchStart) return const layoutMenu = this.getLayoutMenu() if (!layoutMenu) return this._unbindMenuMouseEvents() if (!this._menuMouseEnter) { this._menuMouseEnter = () => { if (this.isSmallScreen() || this._hasClass('layout-transitioning')) { return this._setMenuHoverState(false) } return this._setMenuHoverState(false) } layoutMenu.addEventListener('mouseenter', this._menuMouseEnter, false) layoutMenu.addEventListener('touchstart', this._menuMouseEnter, false) } if (!this._menuMouseLeave) { this._menuMouseLeave = () => { this._setMenuHoverState(false) } layoutMenu.addEventListener('mouseleave', this._menuMouseLeave, false) } if (!this._windowTouchStart) { this._windowTouchStart = e => { if (!e || !e.target || !this._findParent(e.target, '.layout-menu')) { this._setMenuHoverState(false) } } window.addEventListener('touchstart', this._windowTouchStart, true) } }, _unbindMenuMouseEvents() { if (!this._menuMouseEnter && !this._menuMouseLeave && !this._windowTouchStart) return const layoutMenu = this.getLayoutMenu() if (this._menuMouseEnter) { if (layoutMenu) { layoutMenu.removeEventListener('mouseenter', this._menuMouseEnter, false) layoutMenu.removeEventListener('touchstart', this._menuMouseEnter, false) } this._menuMouseEnter = null } if (this._menuMouseLeave) { if (layoutMenu) { layoutMenu.removeEventListener('mouseleave', this._menuMouseLeave, false) } this._menuMouseLeave = null } if (this._windowTouchStart) { if (layoutMenu) { window.addEventListener('touchstart', this._windowTouchStart, true) } this._windowTouchStart = null } this._setMenuHoverState(false) }, // ******************************************************************************* // * Methods scrollToActive(animate = false) { this._scrollToActive(animate) }, // --- // Collapse / expand layout setCollapsed(collapsed = requiredParam('collapsed'), animate = true) { const layoutMenu = this.getLayoutMenu() if (!layoutMenu) return this._unbindLayoutAnimationEndEvent() if (animate && this._supportsTransitionEnd()) { this._addClass('layout-transitioning') if (collapsed) this._setMenuHoverState(false) this._bindLayoutAnimationEndEvent( () => { // Collapse / Expand if (this.isSmallScreen) this._setCollapsed(collapsed) }, () => { this._removeClass('layout-transitioning') this._triggerWindowEvent('resize') this._triggerEvent('toggle') this._setMenuHoverState(false) } ) } else { this._addClass('layout-no-transition') if (collapsed) this._setMenuHoverState(false) // Collapse / Expand this._setCollapsed(collapsed) setTimeout(() => { this._removeClass('layout-no-transition') this._triggerWindowEvent('resize') this._triggerEvent('toggle') this._setMenuHoverState(false) }, 1) } }, // --- // Toggle layout toggleCollapsed(animate = true) { this.setCollapsed(!this.isCollapsed(), animate) }, // --- // Set layout positioning setPosition(fixed = requiredParam('fixed'), offcanvas = requiredParam('offcanvas')) { this._removeClass('layout-menu-offcanvas layout-menu-fixed layout-menu-fixed-offcanvas') if (!fixed && offcanvas) { this._addClass('layout-menu-offcanvas') } else if (fixed && !offcanvas) { this._addClass('layout-menu-fixed') this._redrawLayoutMenu() } else if (fixed && offcanvas) { this._addClass('layout-menu-fixed-offcanvas') this._redrawLayoutMenu() } this.update() }, // ******************************************************************************* // * Getters getLayoutMenu() { return document.querySelector('.layout-menu') }, getMenu() { const layoutMenu = this.getLayoutMenu() if (!layoutMenu) return null return !this._hasClass('menu', layoutMenu) ? layoutMenu.querySelector('.menu') : layoutMenu }, getLayoutNavbar() { return document.querySelector('.layout-navbar') }, getLayoutFooter() { return document.querySelector('.content-footer') }, // ******************************************************************************* // * Update update() { if ( (this.getLayoutNavbar() && ((!this.isSmallScreen() && this.isLayoutNavbarFull() && this.isFixed()) || this.isNavbarFixed())) || (this.getLayoutFooter() && this.isFooterFixed()) ) { this._updateInlineStyle(this._getNavbarHeight(), this._getFooterHeight()) } this._bindMenuMouseEvents() }, setAutoUpdate(enable = requiredParam('enable')) { if (enable && !this._autoUpdate) { this.on('resize.Helpers:autoUpdate', () => this.update()) this._autoUpdate = true } else if (!enable && this._autoUpdate) { this.off('resize.Helpers:autoUpdate') this._autoUpdate = false } }, // ******************************************************************************* // * Tests isRtl() { return ( document.querySelector('body').getAttribute('dir') === 'rtl' || document.querySelector('html').getAttribute('dir') === 'rtl' ) }, isMobileDevice() { return typeof window.orientation !== 'undefined' || navigator.userAgent.indexOf('IEMobile') !== -1 }, isSmallScreen() { return ( (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) < this.LAYOUT_BREAKPOINT ) }, isLayoutNavbarFull() { return !!document.querySelector('.layout-wrapper.layout-navbar-full') }, isCollapsed() { if (this.isSmallScreen()) { return !this._hasClass('layout-menu-expanded') } return this._hasClass('layout-menu-collapsed') }, isFixed() { return this._hasClass('layout-menu-fixed layout-menu-fixed-offcanvas') }, isNavbarFixed() { return ( this._hasClass('layout-navbar-fixed') || (!this.isSmallScreen() && this.isFixed() && this.isLayoutNavbarFull()) ) }, isFooterFixed() { return this._hasClass('layout-footer-fixed') }, isLightStyle() { return document.documentElement.classList.contains('light-style') }, // ******************************************************************************* // * Events on(event = requiredParam('event'), callback = requiredParam('callback')) { const [_event] = event.split('.') let [, ...namespace] = event.split('.') // let [_event, ...namespace] = event.split('.') namespace = namespace.join('.') || null this._listeners.push({ event: _event, namespace, callback }) }, off(event = requiredParam('event')) { const [_event] = event.split('.') let [, ...namespace] = event.split('.') namespace = namespace.join('.') || null this._listeners .filter(listener => listener.event === _event && listener.namespace === namespace) .forEach(listener => this._listeners.splice(this._listeners.indexOf(listener), 1)) }, // ******************************************************************************* // * Life cycle init() { if (this._initialized) return this._initialized = true // Initialize `style` element this._updateInlineStyle(0) // Bind window resize event this._bindWindowResizeEvent() // Bind init event this.off('init._Helpers') this.on('init._Helpers', () => { this.off('resize._Helpers:redrawMenu') this.on('resize._Helpers:redrawMenu', () => { // eslint-disable-next-line no-unused-expressions this.isSmallScreen() && !this.isCollapsed() && this._redrawLayoutMenu() }) // Force repaint in IE 10 if (typeof document.documentMode === 'number' && document.documentMode < 11) { this.off('resize._Helpers:ie10RepaintBody') this.on('resize._Helpers:ie10RepaintBody', () => { if (this.isFixed()) return const { scrollTop } = document.documentElement document.body.style.display = 'none' // document.body.offsetHeight document.body.style.display = 'block' document.documentElement.scrollTop = scrollTop }) } }) this._triggerEvent('init') }, destroy() { if (!this._initialized) return this._initialized = false this._removeClass('layout-transitioning') this._removeInlineStyle() this._unbindLayoutAnimationEndEvent() this._unbindWindowResizeEvent() this._unbindMenuMouseEvents() this.setAutoUpdate(false) this.off('init._Helpers') // Remove all listeners except `init` this._listeners .filter(listener => listener.event !== 'init') .forEach(listener => this._listeners.splice(this._listeners.indexOf(listener), 1)) }, // --- // Init Password Toggle initPasswordToggle() { const toggler = document.querySelectorAll('.form-password-toggle i') if (typeof toggler !== 'undefined' && toggler !== null) { toggler.forEach(el => { el.addEventListener('click', e => { e.preventDefault() const formPasswordToggle = el.closest('.form-password-toggle') const formPasswordToggleIcon = formPasswordToggle.querySelector('i') const formPasswordToggleInput = formPasswordToggle.querySelector('input') if (formPasswordToggleInput.getAttribute('type') === 'text') { formPasswordToggleInput.setAttribute('type', 'password') formPasswordToggleIcon.classList.replace('bx-show', 'bx-hide') } else if (formPasswordToggleInput.getAttribute('type') === 'password') { formPasswordToggleInput.setAttribute('type', 'text') formPasswordToggleIcon.classList.replace('bx-hide', 'bx-show') } }) }) } }, // --- // Init Speech To Text initSpeechToText() { const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition const speechToText = document.querySelectorAll('.speech-to-text') if (SpeechRecognition !== undefined && SpeechRecognition !== null) { if (typeof speechToText !== 'undefined' && speechToText !== null) { const recognition = new SpeechRecognition() const toggler = document.querySelectorAll('.speech-to-text i') toggler.forEach(el => { let listening = false el.addEventListener('click', () => { el.closest('.input-group').querySelector('.form-control').focus() recognition.onspeechstart = () => { listening = true } if (listening === false) { recognition.start() } recognition.onerror = () => { listening = false } recognition.onresult = event => { el.closest('.input-group').querySelector('.form-control').value = event.results[0][0].transcript } recognition.onspeechend = () => { listening = false recognition.stop() } }) }) } } }, // Ajax Call Promise ajaxCall(url) { return new Promise((resolve, reject) => { const req = new XMLHttpRequest() req.open('GET', url) req.onload = () => (req.status === 200 ? resolve(req.response) : reject(Error(req.statusText))) req.onerror = e => reject(Error(`Network Error: ${e}`)) req.send() }) }, // --- // SidebarToggle (Used in Apps) initSidebarToggle() { const sidebarToggler = document.querySelectorAll('[data-bs-toggle="sidebar"]') sidebarToggler.forEach(el => { el.addEventListener('click', () => { const target = el.getAttribute('data-target') const overlay = el.getAttribute('data-overlay') const appOverlay = document.querySelectorAll('.app-overlay') const targetEl = document.querySelectorAll(target) targetEl.forEach(tel => { tel.classList.toggle('show') if ( typeof overlay !== 'undefined' && overlay !== null && overlay !== false && typeof appOverlay !== 'undefined' ) { if (tel.classList.contains('show')) { appOverlay[0].classList.add('show') } else { appOverlay[0].classList.remove('show') } appOverlay[0].addEventListener('click', e => { e.currentTarget.classList.remove('show') tel.classList.remove('show') }) } }) }) }) } } // ******************************************************************************* // * Initialization if (typeof window !== 'undefined') { Helpers.init() if (Helpers.isMobileDevice() && window.chrome) { document.documentElement.classList.add('layout-menu-100vh') } // Update layout after page load if (document.readyState === 'complete') Helpers.update() else document.addEventListener('DOMContentLoaded', function onContentLoaded() { Helpers.update() document.removeEventListener('DOMContentLoaded', onContentLoaded) }) } // --- export { Helpers }