1(function(factory) { 2 if (typeof define === 'function' && define.amd) { 3 define(['jquery'], factory); 4 } else if (typeof module === 'object' && module.exports) { 5 module.exports = function(root, jQuery) { 6 if (jQuery === undefined) { 7 if (typeof window !== 'undefined') { 8 jQuery = require('jquery'); 9 } 10 else { 11 jQuery = require('jquery')(root); 12 } 13 } 14 factory(jQuery); 15 return jQuery; 16 }; 17 } else { 18 factory(jQuery); 19 } 20 }(function($) { 21 var pluginName = 'bootstrapDropdownHover', 22 defaults = { 23 clickBehavior: 'sticky', // Click behavior setting: 24 // 'default' means that the dropdown toggles on hover and on click too 25 // 'disable' disables dropdown toggling with clicking when mouse is detected 26 // 'link' disabled dropdown toggling, but allows link clicks to go through 27 // 'sticky' means if we click on an opened dropdown then it will not hide on 28 // mouseleave but on a second click only 29 hideTimeout: 200 30 }, 31 _hideTimeoutHandler, 32 _hardOpened = false, 33 _touchstartDetected = false, 34 _mouseDetected = false; 35 36 // The actual plugin constructor 37 function BootstrapDropdownHover(element, options) { 38 this.element = $(element); 39 this.settings = $.extend({}, defaults, options, this.element.data()); 40 this._defaults = defaults; 41 this._name = pluginName; 42 this.init(); 43 } 44 45 function getParent($this) { 46 var selector = $this.attr('data-target'); 47 var $parent; 48 49 if (!selector) { 50 selector = $this.attr('href'); 51 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7 52 } 53 54 $parent = selector && $(selector); 55 56 if (!$parent || !$parent.length) {$parent = $this.parent();} 57 58 return $parent; 59 } 60 61 function bindEvents(dropdown) { 62 var $body = $('body'); 63 64 $body.one('touchstart.dropdownhover', function() { 65 _touchstartDetected = true; 66 }); 67 68 $body.one('mousemove.dropdownhover', function() { 69 // touchstart fires before mousemove on touch devices 70 if (!_touchstartDetected) { 71 _mouseDetected = true; 72 } 73 }); 74 75 var $parent = getParent(dropdown.element); 76 77 $('.dropdown-toggle, .dropdown-menu', $parent).on('mouseenter.dropdownhover', function () { 78 // seems to be a touch device 79 if(_mouseDetected && !$(this.hover)) { 80 _mouseDetected = false; 81 } 82 83 if (!_mouseDetected) { 84 return; 85 } 86 87 clearTimeout(_hideTimeoutHandler); 88 if (!$parent.is('.open, .show')) { 89 _hardOpened = false; 90 dropdown.element.dropdown('toggle'); 91 } 92 }); 93 94 $('.dropdown-toggle, .dropdown-menu', $parent).on('mouseleave.dropdownhover', function () { 95 if (!_mouseDetected) { 96 return; 97 } 98 99 if (_hardOpened) { 100 return; 101 } 102 _hideTimeoutHandler = setTimeout(function () { 103 if ($parent.is('.open, .show')) { 104 dropdown.element.dropdown('toggle'); 105 } 106 }, dropdown.settings.hideTimeout); 107 }); 108 109 dropdown.element.on('click.dropdownhover', function (e) { 110 if (dropdown.settings.clickBehavior !== 'link' && !_mouseDetected) { 111 return; 112 } 113 114 switch(dropdown.settings.clickBehavior) { 115 case 'default': 116 return; 117 case 'disable': 118 e.preventDefault(); 119 e.stopImmediatePropagation(); 120 return; 121 case 'link': 122 e.stopImmediatePropagation(); 123 return; 124 case 'sticky': 125 if (_hardOpened) { 126 _hardOpened = false; 127 } 128 else { 129 _hardOpened = true; 130 if ($parent.is('.open, .show')) { 131 e.stopImmediatePropagation(); 132 e.preventDefault(); 133 } 134 } 135 return; 136 } 137 }); 138 } 139 140 function removeEvents(dropdown) { 141 var $parent = getParent(dropdown.element); 142 $('.dropdown-toggle, .dropdown-menu', $parent).off('.dropdownhover'); 143 // seems that bootstrap binds the click handler twice after we reinitializing the plugin after a destroy... 144 $('.dropdown-toggle, .dropdown-menu', $parent).off('.dropdown'); 145 dropdown.element.off('.dropdownhover'); 146 $('body').off('.dropdownhover'); 147 } 148 149 BootstrapDropdownHover.prototype = { 150 init: function () { 151 this.setClickBehavior(this.settings.clickBehavior); 152 this.setHideTimeout(this.settings.hideTimeout); 153 bindEvents(this); 154 return this.element; 155 }, 156 setClickBehavior: function(value) { 157 this.settings.clickBehavior = value; 158 return this.element; 159 }, 160 setHideTimeout: function(value) { 161 this.settings.hideTimeout = value; 162 return this.element; 163 }, 164 destroy: function() { 165 clearTimeout(_hideTimeoutHandler); 166 removeEvents(this); 167 this.element.data('plugin_' + pluginName, null); 168 return this.element; 169 } 170 }; 171 172 // A really lightweight plugin wrapper around the constructor, 173 // preventing against multiple instantiations 174 $.fn[pluginName] = function (options) { 175 var args = arguments; 176 177 // Is the first parameter an object (options), or was omitted, instantiate a new instance of the plugin. 178 if (options === undefined || typeof options === 'object') { 179 // This allows the plugin to be called with $.fn.bootstrapDropdownHover(); 180 if (!$.contains(document, $(this)[0])) { 181 $('[data-toggle="dropdown"]').each(function (index, item) { 182 // For each nested select, instantiate the plugin 183 $(item).bootstrapDropdownHover(options); 184 }); 185 } 186 return this.each(function () { 187 // If this is not a select 188 if (!$(this).hasClass('dropdown-toggle') || $(this).data('toggle') !== 'dropdown') { 189 $('[data-toggle="dropdown"]', this).each(function (index, item) { 190 // For each nested select, instantiate the plugin 191 $(item).bootstrapDropdownHover(options); 192 }); 193 } else if (!$.data(this, 'plugin_' + pluginName)) { 194 // Only allow the plugin to be instantiated once so we check that the element has no plugin instantiation yet 195 196 // if it has no instance, create a new one, pass options to our plugin constructor, 197 // and store the plugin instance in the elements jQuery data object. 198 $.data(this, 'plugin_' + pluginName, new BootstrapDropdownHover(this, options)); 199 } 200 }); 201 202 // If the first parameter is a string and it doesn't start with an underscore or "contains" the `init`-function, 203 // treat this as a call to a public method. 204 } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { 205 206 // Cache the method call to make it possible to return a value 207 var returns; 208 209 this.each(function () { 210 var instance = $.data(this, 'plugin_' + pluginName); 211 // Tests that there's already a plugin-instance and checks that the requested public method exists 212 if (instance instanceof BootstrapDropdownHover && typeof instance[options] === 'function') { 213 // Call the method of our plugin instance, and pass it the supplied arguments. 214 returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1)); 215 } 216 }); 217 218 // If the earlier cached method gives a value back return the value, 219 // otherwise return this to preserve chainability. 220 return returns !== undefined ? returns : this; 221 } 222 223 }; 224 225 })); 226
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.