851 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			851 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // Constants
 | |
| var TRANS_EVENTS = ['transitionend', 'webkitTransitionEnd', 'oTransitionEnd']
 | |
| var TRANS_PROPERTIES = ['transition', 'MozTransition', 'webkitTransition', 'WebkitTransition', 'OTransition']
 | |
| var 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}\`` : ''}`)
 | |
| }
 | |
| 
 | |
| var 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 }
 | 
