1 /* ========================================================================
2 * Bootstrap Dropdowns Enhancement: dropdowns-enhancement.js v3.1.1 (Beta 1)
3 * http://behigh.github.io/bootstrap_dropdowns_enhancement/
4 * ========================================================================
5 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
6 * ======================================================================== */
11 var toggle = '[data-toggle="dropdown"]',
12 disabled = '.disabled, :disabled',
13 backdrop = '.dropdown-backdrop',
14 menuClass = 'dropdown-menu',
15 subMenuClass = 'dropdown-submenu',
16 namespace = '.bs.dropdown.data-api',
17 eventNamespace = '.bs.dropdown',
19 touchSupport = 'ontouchstart' in document.documentElement,
23 function Dropdown(element) {
24 $(element).on('click' + eventNamespace, this.toggle)
27 var proto = Dropdown.prototype;
29 proto.toggle = function(event) {
30 var $element = $(this);
32 if ($element.is(disabled)) return;
34 var $parent = getParent($element);
35 var isActive = $parent.hasClass(openClass);
36 var isSubMenu = $parent.hasClass(subMenuClass);
37 var menuTree = isSubMenu ? getSubMenuParents($parent) : null;
39 closeOpened(event, menuTree);
45 if (touchSupport && !$parent.closest('.navbar-nav').length && !menuTree[0].find(backdrop).length) {
46 // if mobile we use a backdrop because click events don't delegate
47 $('<div class="' + backdrop.substr(1) + '"/>').appendTo(menuTree[0]).on('click', closeOpened)
50 for (var i = 0, s = menuTree.length; i < s; i++) {
51 if (!menuTree[i].hasClass(openClass)) {
52 menuTree[i].addClass(openClass);
53 positioning(menuTree[i].children('.' + menuClass), menuTree[i]);
62 proto.keydown = function (e) {
63 if (!/(38|40|27)/.test(e.keyCode)) return;
70 if ($this.is('.disabled, :disabled')) return;
72 var $parent = getParent($this);
73 var isActive = $parent.hasClass('open');
75 if (!isActive || (isActive && e.keyCode == 27)) {
76 if (e.which == 27) $parent.find(toggle).trigger('focus');
77 return $this.trigger('click')
80 var desc = ' li:not(.divider):visible a';
81 var desc1 = 'li:not(.divider):visible > input:not(disabled) ~ label';
82 var $items = $parent.find(desc1 + ', ' + '[role="menu"]' + desc + ', [role="listbox"]' + desc);
84 if (!$items.length) return;
86 var index = $items.index($items.filter(':focus'));
88 if (e.keyCode == 38 && index > 0) index--; // up
89 if (e.keyCode == 40 && index < $items.length - 1) index++; // down
90 if (!~index) index = 0;
92 $items.eq(index).trigger('focus')
95 proto.change = function (e) {
105 $menu = $(this).closest('.' + menuClass);
107 $toggle = $menu.parent().find('[data-label-placement]');
109 if (!$toggle || !$toggle.length) {
110 $toggle = $menu.parent().find(toggle);
113 if (!$toggle || !$toggle.length || $toggle.data('placeholder') === false)
114 return; // do nothing, no control
116 ($toggle.data('placeholder') == undefined && $toggle.data('placeholder', $.trim($toggle.text())));
117 text = $.data($toggle[0], 'placeholder');
119 $items = $menu.find('li > input:checked');
123 $items.each(function () {
124 var str = $(this).parent().find('label').eq(0),
125 label = str.find('.data-label');
128 var p = $('<p></p>');
129 p.append(label.clone());
137 str && text.push($.trim(str));
140 text = text.length < 4 ? text.join(', ') : text.length + ' selected';
143 var caret = $toggle.find('.caret');
145 $toggle.html(text || ' ');
147 $toggle.append(' ') && caret.appendTo($toggle);
151 function positioning($menu, $control) {
152 if ($menu.hasClass('pull-center')) {
153 $menu.css('margin-right', $menu.outerWidth() / -2);
156 if ($menu.hasClass('pull-middle')) {
157 $menu.css('margin-top', ($menu.outerHeight() / -2) - ($control.outerHeight() / 2));
161 function closeOpened(event, menuTree) {
170 if (opened[0] !== menuTree[0][0]) {
173 parent = menuTree[menuTree.length - 1];
174 if (parent.parent().hasClass(menuClass)) {
175 parent = parent.parent();
179 parent.find('.' + openClass).removeClass(openClass);
181 if (parent.hasClass(openClass))
182 parent.removeClass(openClass);
184 if (parent === opened) {
186 $(backdrop).remove();
191 function getSubMenuParents($submenu) {
192 var result = [$submenu];
194 while (!$parent || $parent.hasClass(subMenuClass)) {
195 $parent = ($parent || $submenu).parent();
196 if ($parent.hasClass(menuClass)) {
197 $parent = $parent.parent();
199 if ($parent.children(toggle)) {
200 result.unshift($parent);
206 function getParent($this) {
207 var selector = $this.attr('data-target');
210 selector = $this.attr('href');
211 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7
214 var $parent = selector && $(selector);
216 return $parent && $parent.length ? $parent : $this.parent()
219 // DROPDOWN PLUGIN DEFINITION
220 // ==========================
222 var old = $.fn.dropdown;
224 $.fn.dropdown = function (option) {
225 return this.each(function () {
227 var data = $this.data('bs.dropdown');
229 if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)));
230 if (typeof option == 'string') data[option].call($this);
234 $.fn.dropdown.Constructor = Dropdown;
236 $.fn.dropdown.clearMenus = function(e) {
237 $(backdrop).remove();
238 $('.' + openClass + ' ' + toggle).each(function () {
239 var $parent = getParent($(this));
240 var relatedTarget = { relatedTarget: this };
241 if (!$parent.hasClass('open')) return;
242 $parent.trigger(e = $.Event('hide' + eventNamespace, relatedTarget));
243 if (e.isDefaultPrevented()) return;
244 $parent.removeClass('open').trigger('hidden' + eventNamespace, relatedTarget);
250 // DROPDOWN NO CONFLICT
251 // ====================
253 $.fn.dropdown.noConflict = function () {
259 $(document).off(namespace)
260 .on('click' + namespace, closeOpened)
261 .on('click' + namespace, toggle, proto.toggle)
262 .on('click' + namespace, '.dropdown-menu > li > input[type="checkbox"] ~ label, .dropdown-menu > li > input[type="checkbox"], .dropdown-menu.noclose > li', function (e) {
265 .on('change' + namespace, '.dropdown-menu > li > input[type="checkbox"], .dropdown-menu > li > input[type="radio"]', proto.change)
266 .on('keydown' + namespace, toggle + ', [role="menu"], [role="listbox"]', proto.keydown)