/**
* Bootstrap Dorpdown Wrapper. * * @author Htmlstream * @version 1.0 * */
;(function($){
'use strict'; /** * Helper function for detect support of touch events. * * @return Boolean */ function touchEvents(){ if('ontouchstart' in window) { return true; } return false; } /** * Helper function for moving viewport to specified element. * * @param jQuery element * * @return undefined */ function focusElement(element){ if(!element || !element.length) return; var header = element.closest('.u-header').filter('[class*="u-header--side"]'), container = header.find('.u-header__sections-container'), offset = element.offset().top + container.scrollTop(), breakpoint = $(window).height() / 2; if(header.length && offset > breakpoint) { container.stop().animate({ scrollTop: offset - breakpoint }); } } /** * Adds class if element doesn't fit into the screen. * * @param jQuery element * * @return undefined */ function smartPosition(element) { if(!element || !element.length) return; element.removeClass('u-dropdown--reverse-position').show(); var breakpoint = $(window).width(), offset = element.offset().left + element.outerWidth(); element.hide(); if(offset > breakpoint) { element.addClass('u-dropdown--reverse-position'); } // need RTL implementation } $.HSCore.components.HSDropdown = { /** * Base configuration of the wrapper. * * @var Object _baseConfig */ _baseConfig: { event: 'click', // 'click', 'hover' appear: 'show-hide' // 'show-hide', 'slide', 'fade', 'slideRight' }, /** * Initialization of HSDropdown wrapper. * * @param String collection (optional) * * @return jQuery - collection of initialized items. */ init: function(collection) { if(!collection && !collection.length) return; var self = this; return collection.each(function(i, el){ var $this = $(el), config = $.extend(true, {}, self._baseConfig, $this.data()); if($this.data('HSDropdown')) return; $this.data( 'HSDropdown', self._makeDropdownObject($this, config) ); }); }, /** * Returns Dropdown Object * * @param jQuery element * @param Object config * * @return OnHoverDropdown|OnClickDropdown */ _makeDropdownObject: function(element, config) { switch(config['event']) { case 'hover' : return new OnHoverDropdown( element, config, this._makeDropdownEffect(element, config) ); break; default: return new OnClickDropdown( element, config, this._makeDropdownEffect(element, config) ); } }, /** * Returns dropdown effect object. * * @param jQuery element * @param Object config * * @return SlideDownDropdownEffect|ShowHideDropdownEffect|FadeToggleDropdownEffect */ _makeDropdownEffect: function(element, config) { switch(config['appear']) { case 'slide': return new SlideDownDropdownEffect(element); break; case 'slideRight': return new SlideRightDropdownEffect(element); break; case 'fade': return new FadeToggleDropdownEffect(element); break; default: return new ShowHideDropdownEffect(element); } } }; /** * Constructor function for dropdown elements which will be appear by hover event. * * @param jQuery element * @param Object config * @param Object effect */ function OnHoverDropdown(element, config, effect) { this.element = element; this.config = config; this.effect = effect; if(touchEvents()) { this.initTabletBehavior(); } else { this.init(); } } /** * Initialization of OnHoverDropdown object. * * @return OnHoverDropdown */ OnHoverDropdown.prototype.init = function() { var self = this; this.element .on('mouseenter.HSDropdown', function(e){ if(self.mouseLeaveTimeOutId) clearTimeout(self.mouseLeaveTimeOutId); self.effect.show(); e.preventDefault(); }) .on('mouseleave.HSDropdown', function(e){ self.mouseLeaveTimeOutId = setTimeout(function(){ self.effect.hide(); }, 100); e.preventDefault(); }) .on('click.HSDropdown', function(e){ e.stopPropagation(); }) .siblings('.dropdown-menu') .on('mouseenter.HSDropdown', function(e){ if(self.mouseLeaveTimeOutId) clearTimeout(self.mouseLeaveTimeOutId); e.preventDefault(); }) .on('mouseleave.HSDropdown', function(e){ self.mouseLeaveTimeOutId = setTimeout(function(){ self.effect.hide(); }, 100); e.preventDefault(); }); return this; } /** * Initialization tablet behavior of OnHoverDropdown object. * * @return OnHoverDropdown */ OnHoverDropdown.prototype.initTabletBehavior = function(){ var self = this; this.element.on('click.HSDropdown', function(e){ if(!self.element.data('showed')) { self.element.data('showed', true); self.effect.show(); } else { self.element.data('showed', false); self.effect.hide(); } e.preventDefault(); e.stopPropagation(); }); $(document).on('click.HSDropdown', function(e){ if(!$(e.target).closest(self.element.parent()).length) { self.element.data('showed', false); self.effect.hide(); } }); return this; } /** * Destroy OnHoverDropdown object. * * @return OnHoverDropdown */ OnHoverDropdown.prototype.destroy = function() { var self = this; this.element .off('.HSDropdown') .siblings('.dropdown-menu') .off('.HSDropdown'); $(document).off('.HSDropdown'); return this; } /** * Constructor function for dropdown elements which will be appear by click event. * * @param jQuery element * @param Object config * @param Object effect */ function OnClickDropdown(element, config, effect) { this.element = element; this.config = config; this.effect = effect; this.init(); } /** * * * @param * * @return OnClickDropdown */ OnClickDropdown.prototype.init = function() { var self = this; this.element.on('click.HSDropdown', function(e){ var opened = $('[data-toggle="dropdown"]').not(self.element) if(opened.length) { opened.data('HSDropdown').effect.hide(); opened.data('HSDropdown').element.data('showed', false); } if(!self.element.data('showed')) { self.element.data('showed', true); self.effect.show(); } else { self.element.data('showed', false); self.effect.hide(); } e.preventDefault(); e.stopPropagation(); }); $(document).on('click.HSDropdown', function(e){ if(!$(e.target).closest(self.element.parent()).length) { self.element.data('showed', false); self.effect.hide(); } }); return this; } /** * * * @param * * @return OnClickDropdown */ OnClickDropdown.prototype.destroy = function() { this.element .off('.HSDropdown'); $(document).off('.HSDropdown'); return this; } /** * Constructor function for making dropdown effect object. * * @param jQuery element */ function ShowHideDropdownEffect(element) { this.element = element; this.dropdown = this.element.siblings('.dropdown-menu'); } /** * Shows dropdown element. * * @return ShowHideDropdownEffect */ ShowHideDropdownEffect.prototype.show = function() { var inlineNavbar = this.element.closest('[class*="u-navbar--inline-submenu"]'); if(!this.dropdown.length) return this; this.element.parent().addClass('show'); if(inlineNavbar.length) { inlineNavbar.removeClass('u-navbar--overflow'); var ddOffset = this.dropdown.offset().left; if(ddOffset < 0) inlineNavbar.addClass('u-navbar--overflow'); } focusElement(this.element); return this; } /** * Hides dropdown element. * * @return ShowHideDropdownEffect */ ShowHideDropdownEffect.prototype.hide = function() { this.element.parent().removeClass('show'); return this; } /** * Constructor function for making dropdown effect object. * * @param jQuery element */ function FadeToggleDropdownEffect(element) { this.element = element; this.dropdown = this.element.siblings('.dropdown-menu'); } /** * Shows dropdown element. * * @return FadeToggleDropdownEffect */ FadeToggleDropdownEffect.prototype.show = function() { var self = this, inlineNavbar = this.element.closest('[class*="u-navbar--inline-submenu"]'); if(!this.dropdown.length) return this; if(inlineNavbar.length) { inlineNavbar.removeClass('u-navbar--overflow'); this.dropdown.show(); var ddOffset = this.dropdown.offset().left; this.dropdown.hide(); if(ddOffset < 0) inlineNavbar.addClass('u-navbar--overflow'); } this.dropdown.stop().fadeIn({ easing: self.element.data('appear-easing') ? self.element.data('appear-easing') : 'linear', duration: isFinite(self.element.data('appear-speed')) ? self.element.data('appear-speed') : 350, complete: function() { focusElement(self.element); } }); return this; } /** * Hides dropdown element. * * @return FadeToggleDropdownEffect */ FadeToggleDropdownEffect.prototype.hide = function() { var self = this; this.dropdown.stop().fadeOut({ easing: self.element.data('appear-easing') ? self.element.data('appear-easing') : 'linear', duration: isFinite(self.element.data('appear-speed')) ? self.element.data('appear-speed') : 350 }); return this; } /** * Constructor function for making dropdown effect object. * * @param jQuery element */ function SlideDownDropdownEffect(element) { this.element = element; this.dropdown = this.element.siblings('.dropdown-menu'); } /** * Shows dropdown element. * * @return SlideDownDropdownEffect */ SlideDownDropdownEffect.prototype.show = function() { var self = this, inlineNavbar = this.element.closest('[class*="u-navbar--inline-submenu"]'); if(!this.dropdown.length) return this; if(inlineNavbar.length) { inlineNavbar.removeClass('u-navbar--overflow'); this.dropdown.show(); var ddOffset = this.dropdown.offset().left; this.dropdown.hide(); if(ddOffset < 0) inlineNavbar.addClass('u-navbar--overflow'); } smartPosition(this.dropdown); this.dropdown.stop().slideDown({ easing: self.element.data('appear-easing') ? self.element.data('appear-easing') : 'linear', duration: isFinite(self.element.data('appear-speed')) ? self.element.data('appear-speed') : 350, complete: function() { focusElement(self.element); } }); return this; } /** * Hides dropdown element. * * @return SlideDownDropdownEffect */ SlideDownDropdownEffect.prototype.hide = function() { var self = this; this.dropdown.stop().slideUp({ easing: self.element.data('appear-easing') ? self.element.data('appear-easing') : 'linear', duration: isFinite(self.element.data('appear-speed')) ? self.element.data('appear-speed') : 350 }); return this; } /** * Constructor function for making dropdown effect object. * * @param jQuery element */ function SlideRightDropdownEffect(element) { this.element = element; this.dropdown = this.element.siblings('.dropdown-menu'); this.w = $(window); this.toggleable = this.element.closest('[class*="navbar-toggleable-"]'); if(this.toggleable.hasClass('navbar-toggleable-lg')) { this.breakpoint = 1200; } else if(this.toggleable.hasClass('navbar-toggleable-md')) { this.breakpoint = 992; } else if(this.toggleable.hasClass('navbar-toggleable-sm')) { this.breakpoint = 768; } } /** * Shows dropdown element. * * @return SlideRightDropdownEffect */ SlideRightDropdownEffect.prototype.show = function() { var self = this; this.dropdown.css('width', 'auto') .data('initial-width', this.dropdown.outerWidth()) .css({ 'width': 0, 'min-width': 'initial' }); if(this.breakpoint && this.w.width() > this.breakpoint) { this.dropdown.show().stop().animate({ width: self.dropdown.data('initial-width') }, { easing: self.element.data('appear-easing') ? self.element.data('appear-easing') : 'linear', duration: isFinite(self.element.data('appear-speed')) ? self.element.data('appear-speed') : 350 }); } else{ this.dropdown.css('width', 'auto').stop().slideDown({ easing: self.element.data('appear-easing') ? self.element.data('appear-easing') : 'linear', duration: isFinite(self.element.data('appear-speed')) ? self.element.data('appear-speed') : 350 }); } return this; } /** * Hides dropdown element. * * @return SlideRightDropdownEffect */ SlideRightDropdownEffect.prototype.hide = function() { var self = this; if(this.breakpoint && this.w.width() > this.breakpoint) { this.dropdown.stop().animate({ width: 0 }, { easing: self.element.data('appear-easing') ? self.element.data('appear-easing') : 'linear', duration: isFinite(self.element.data('appear-speed')) ? self.element.data('appear-speed') : 350, complete: function() { $(this).hide(); } }); } else{ this.dropdown.stop().slideUp({ easing: self.element.data('appear-easing') ? self.element.data('appear-easing') : 'linear', duration: isFinite(self.element.data('appear-speed')) ? self.element.data('appear-speed') : 350 }); } return this; }
})(jQuery);