1'use strict'; 2 3var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 4 5function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6 7!function ($) { 8 9 /** 10 * DropdownMenu module. 11 * @module foundation.dropdown-menu 12 * @requires foundation.util.keyboard 13 * @requires foundation.util.box 14 * @requires foundation.util.nest 15 */ 16 17 var DropdownMenu = function () { 18 /** 19 * Creates a new instance of DropdownMenu. 20 * @class 21 * @fires DropdownMenu#init 22 * @param {jQuery} element - jQuery object to make into a dropdown menu. 23 * @param {Object} options - Overrides to the default plugin settings. 24 */ 25 function DropdownMenu(element, options) { 26 _classCallCheck(this, DropdownMenu); 27 28 this.$element = element; 29 this.options = $.extend({}, DropdownMenu.defaults, this.$element.data(), options); 30 31 Foundation.Nest.Feather(this.$element, 'dropdown'); 32 this._init(); 33 34 Foundation.registerPlugin(this, 'DropdownMenu'); 35 Foundation.Keyboard.register('DropdownMenu', { 36 'ENTER': 'open', 37 'SPACE': 'open', 38 'ARROW_RIGHT': 'next', 39 'ARROW_UP': 'up', 40 'ARROW_DOWN': 'down', 41 'ARROW_LEFT': 'previous', 42 'ESCAPE': 'close' 43 }); 44 } 45 46 /** 47 * Initializes the plugin, and calls _prepareMenu 48 * @private 49 * @function 50 */ 51 52 53 _createClass(DropdownMenu, [{ 54 key: '_init', 55 value: function _init() { 56 var subs = this.$element.find('li.is-dropdown-submenu-parent'); 57 this.$element.children('.is-dropdown-submenu-parent').children('.is-dropdown-submenu').addClass('first-sub'); 58 59 this.$menuItems = this.$element.find('[role="menuitem"]'); 60 this.$tabs = this.$element.children('[role="menuitem"]'); 61 this.$tabs.find('ul.is-dropdown-submenu').addClass(this.options.verticalClass); 62 63 if (this.$element.hasClass(this.options.rightClass) || this.options.alignment === 'right' || Foundation.rtl() || this.$element.parents('.top-bar-right').is('*')) { 64 this.options.alignment = 'right'; 65 subs.addClass('opens-left'); 66 } else { 67 subs.addClass('opens-right'); 68 } 69 this.changed = false; 70 this._events(); 71 } 72 }, { 73 key: '_isVertical', 74 value: function _isVertical() { 75 return this.$tabs.css('display') === 'block'; 76 } 77 78 /** 79 * Adds event listeners to elements within the menu 80 * @private 81 * @function 82 */ 83 84 }, { 85 key: '_events', 86 value: function _events() { 87 var _this = this, 88 hasTouch = 'ontouchstart' in window || typeof window.ontouchstart !== 'undefined', 89 parClass = 'is-dropdown-submenu-parent'; 90 91 // used for onClick and in the keyboard handlers 92 var handleClickFn = function handleClickFn(e) { 93 var $elem = $(e.target).parentsUntil('ul', '.' + parClass), 94 hasSub = $elem.hasClass(parClass), 95 hasClicked = $elem.attr('data-is-click') === 'true', 96 $sub = $elem.children('.is-dropdown-submenu'); 97 98 if (hasSub) { 99 if (hasClicked) { 100 if (!_this.options.closeOnClick || !_this.options.clickOpen && !hasTouch || _this.options.forceFollow && hasTouch) { 101 return; 102 } else { 103 e.stopImmediatePropagation(); 104 e.preventDefault(); 105 _this._hide($elem); 106 } 107 } else { 108 e.preventDefault(); 109 e.stopImmediatePropagation(); 110 _this._show($sub); 111 $elem.add($elem.parentsUntil(_this.$element, '.' + parClass)).attr('data-is-click', true); 112 } 113 } 114 }; 115 116 if (this.options.clickOpen || hasTouch) { 117 this.$menuItems.on('click.zf.dropdownmenu touchstart.zf.dropdownmenu', handleClick
vendor: 5,487 bytes, lines 117-282
117Fn); 118 } 119 120 // Handle Leaf element Clicks 121 if (_this.options.closeOnClickInside) { 122 this.$menuItems.on('click.zf.dropdownmenu', function (e) { 123 var $elem = $(this), 124 hasSub = $elem.hasClass(parClass); 125 if (!hasSub) { 126 _this._hide(); 127 } 128 }); 129 } 130 131 if (!this.options.disableHover) { 132 this.$menuItems.on('mouseenter.zf.dropdownmenu', function (e) { 133 var $elem = $(this), 134 hasSub = $elem.hasClass(parClass); 135 136 if (hasSub) { 137 clearTimeout($elem.data('_delay')); 138 $elem.data('_delay', setTimeout(function () { 139 _this._show($elem.children('.is-dropdown-submenu')); 140 }, _this.options.hoverDelay)); 141 } 142 }).on('mouseleave.zf.dropdownmenu', function (e) { 143 var $elem = $(this), 144 hasSub = $elem.hasClass(parClass); 145 if (hasSub && _this.options.autoclose) { 146 if ($elem.attr('data-is-click') === 'true' && _this.options.clickOpen) { 147 return false; 148 } 149 150 clearTimeout($elem.data('_delay')); 151 $elem.data('_delay', setTimeout(function () { 152 _this._hide($elem); 153 }, _this.options.closingTime)); 154 } 155 }); 156 } 157 this.$menuItems.on('keydown.zf.dropdownmenu', function (e) { 158 var $element = $(e.target).parentsUntil('ul', '[role="menuitem"]'), 159 isTab = _this.$tabs.index($element) > -1, 160 $elements = isTab ? _this.$tabs : $element.siblings('li').add($element), 161 $prevElement, 162 $nextElement; 163 164 $elements.each(function (i) { 165 if ($(this).is($element)) { 166 $prevElement = $elements.eq(i - 1); 167 $nextElement = $elements.eq(i + 1); 168 return; 169 } 170 }); 171 172 var nextSibling = function nextSibling() { 173 if (!$element.is(':last-child')) { 174 $nextElement.children('a:first').focus(); 175 e.preventDefault(); 176 } 177 }, 178 prevSibling = function prevSibling() { 179 $prevElement.children('a:first').focus(); 180 e.preventDefault(); 181 }, 182 openSub = function openSub() { 183 var $sub = $element.children('ul.is-dropdown-submenu'); 184 if ($sub.length) { 185 _this._show($sub); 186 $element.find('li > a:first').focus(); 187 e.preventDefault(); 188 } else { 189 return; 190 } 191 }, 192 closeSub = function closeSub() { 193 //if ($element.is(':first-child')) { 194 var close = $element.parent('ul').parent('li'); 195 close.children('a:first').focus(); 196 _this._hide(close); 197 e.preventDefault(); 198 //} 199 }; 200 var functions = { 201 open: openSub, 202 close: function close() { 203 _this._hide(_this.$element); 204 _this.$menuItems.find('a:first').focus(); // focus to first element 205 e.preventDefault(); 206 }, 207 handled: function handled() { 208 e.stopImmediatePropagation(); 209 } 210 }; 211 212 if (isTab) { 213 if (_this._isVertical()) { 214 // vertical menu 215 if (Foundation.rtl()) { 216 // right aligned 217 $.extend(functions, { 218 down: nextSibling, 219 up: prevSibling, 220 next: closeSub, 221 previous: openSub 222 }); 223 } else { 224 // left aligned 225 $.extend(functions, { 226 down: nextSibling, 227 up: prevSibling, 228 next: openSub, 229 previous: closeSub 230 }); 231 } 232 } else { 233 // horizontal menu 234 if (Foundation.rtl()) { 235 // right aligned 236 $.extend(functions, { 237 next: prevSibling, 238 previous: nextSibling, 239 down: openSub, 240 up: closeSub 241 }); 242 } else { 243 // left aligned 244 $.extend(functions, { 245 next: nextSibling, 246 previous: prevSibling, 247 down: openSub, 248 up: closeSub 249 }); 250 } 251 } 252 } else { 253 // not tabs -> one sub 254 if (Foundation.rtl()) { 255 // right aligned 256 $.extend(functions, { 257 next: closeSub, 258 previous: openSub, 259 down: nextSibling, 260 up: prevSibling 261 }); 262 } else { 263 // left aligned 264 $.extend(functions, { 265 next: openSub, 266 previous: closeSub, 267 down: nextSibling, 268 up: prevSibling 269 }); 270 } 271 } 272 Foundation.Keyboard.handleKey(e, 'DropdownMenu', functions); 273 }); 274 } 275 276 /** 277 * Adds an event handler to the body to close any dropdowns on a click. 278 * @function 279 * @private 280 */ 281 282 }, {
283 key: '_addBodyHandler', 284 value: function _addBodyHandler() { 285 var $body = $(document.body), 286 _this = this; 287 $body.off('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu').on('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu', function (e) { 288 var $link = _this.$element.find(e.target); 289 if ($link.length) { 290 return; 291 } 292 293 _this._hide(); 294 $body.off('mouseup.zf.dropdownmenu touchend.zf.dropdownmenu'); 295 }); 296 } 297 298 /** 299 * Opens a dropdown pane, and checks for collisions first. 300 * @param {jQuery} $sub - ul element that is a submenu to show 301 * @function 302 * @private 303 * @fires DropdownMenu#show 304 */ 305 306 }, { 307 key: '_show', 308 value: function _show($sub) { 309 var idx = this.$tabs.index(this.$tabs.filter(function (i, el) { 310 return $(el).find($sub).length > 0; 311 })); 312 var $sibs = $sub.parent('li.is-dropdown-submenu-parent').siblings('li.is-dropdown-submenu-parent'); 313 this._hide($sibs, idx); 314 $sub.css('visibility', 'hidden').addClass('js-dropdown-active').parent('li.is-dropdown-submenu-parent').addClass('is-active'); 315 var clear = Foundation.Box.ImNotTouchingYou($sub, null, true); 316 if (!clear) { 317 var oldClass = this.options.alignment === 'left' ? '-right' : '-left', 318 $parentLi = $sub.parent('.is-dropdown-submenu-parent'); 319 $parentLi.removeClass('opens' + oldClass).addClass('opens-' + this.options.alignment); 320 clear = Foundation.Box.ImNotTouchingYou($sub, null, true); 321 if (!clear) { 322 $parentLi.removeClass('opens-' + this.options.alignment).addClass('opens-inner'); 323 } 324 this.changed = true; 325 } 326 $sub.css('visibility', ''); 327 if (this.options.closeOnClick) { 328 this._addBodyHandler(); 329 } 330 /** 331 * Fires when the new dropdown pane is visible. 332 * @event DropdownMenu#show 333 */ 334 this.$element.trigger('show.zf.dropdownmenu', [$sub]); 335 } 336 337 /** 338 * Hides a single, currently open dropdown pane, if passed a parameter, otherwise, hides everything. 339 * @function 340 * @param {jQuery} $elem - element with a submenu to hide 341 * @param {Number} idx - index of the $tabs collection to hide 342 * @private 343 */ 344 345 }, { 346 key: '_hide', 347 value: function _hide($elem, idx) { 348 var $toClose; 349 if ($elem && $elem.length) { 350 $toClose = $elem; 351 } else if (idx !== undefined) { 352 $toClose = this.$tabs.not(function (i, el) { 353 return i === idx; 354 }); 355 } else { 356 $toClose = this.$element; 357 } 358 var somethingToClose = $toClose.hasClass('is-active') || $toClose.find('.is-active').length > 0; 359 360 if (somethingToClose) { 361 $toClose.find('li.is-active').add($toClose).attr({ 362 'data-is-click': false 363 }).removeClass('is-active'); 364 365 $toClose.find('ul.js-dropdown-active').removeClass('js-dropdown-active'); 366 367 if (this.changed || $toClose.find('opens-inner').length) { 368 var oldClass = this.options.alignment === 'left' ? 'right' : 'left'; 369 $toClose.find('li.is-dropdown-submenu-parent').add($toClose).removeClass('opens-inner opens-' + this.options.alignment).addClass('opens-' + oldClass); 370 this.changed = false; 371 } 372 /** 373 * Fires when the open menus are closed. 374 * @event DropdownMenu#hide 375 */ 376 this.$element.trigger('hide.zf.dropdownmenu', [$toClose]); 377 } 378 } 379 380 /** 381 * Destroys the plugin. 382 * @function 383 */ 384 385 }, { 386 key: 'destroy', 387 value: function destroy() { 388 this.$menuItems.off('.zf.dropdownmenu').removeAttr('data-is-click').removeClass('is-right-arrow is-left-arrow is-down-arrow opens-right opens-left opens-inner'); 389 $(document.body).off('.zf.dropdownmenu'); 390 Foundation.Nest.Burn(this.$element, 'dropdown'); 391 Foundation.unregisterPlugin(this); 392 } 393 }]); 394 395 return DropdownMenu; 396 }(); 397 398 /** 399 * Default settings for plugin 400 */ 401 402 403 DropdownMenu.defaults = { 404 /** 405 * Disallows hover events from opening submenus 406 * @option 407 * @type {boolean} 408 * @default false 409 */ 410 disableHover: false, 411 /** 412 * Allow a submenu to automatically close on a mouseleave event, if not clicked open. 413 * @option 414 * @type {boolean} 415 * @default true 416 */ 417 autoclose: true, 418 /** 419 * Amount of time to delay opening a submenu on hover event. 420 * @option 421 * @type {number} 422 * @default 50 423 */ 424 hoverDelay: 50, 425 /** 426 * Allow a submenu to open/remain open on parent click event. Allows cursor to move away from menu. 427 * @option 428 * @type {boolean} 429 * @default false 430 */ 431 clickOpen: false, 432 /** 433 * Amount of time to delay closing a submenu on a mouseleave event. 434 * @option 435 * @type {number} 436 * @default 500 437 */ 438 439 closingTime: 500, 440 /** 441 * Position of the menu relative to what direction the submenus should open. Handled by JS. Can be `'left'` or `'right'`. 442 * @option 443 * @type {string} 444 * @default 'left' 445 */ 446 alignment: 'left', 447 /** 448 * Allow clicks on the body to close any open submenus. 449 * @option 450 * @type {boolean} 451 * @default true 452 */ 453 closeOnClick: true, 454 /** 455 * Allow clicks on leaf anchor links to close any open submenus. 456 * @option 457 * @type {boolean} 458 * @default true 459 */ 460 closeOnClickInside: true, 461 /** 462 * Class applied to vertical oriented menus, Foundation default is `vertical`. Update this if using your own class. 463 * @option 464 * @type {string} 465 * @default 'vertical' 466 */ 467 verticalClass: 'vertical', 468 /** 469 * Class applied to right-side oriented menus, Foundation default is `align-right`. Update this if using your own class. 470 * @option 471 * @type {string} 472 * @default 'align-right' 473 */ 474 rightClass: 'align-right', 475 /** 476 * Boolean to force overide the clicking of links to perform default action, on second touch event for mobile. 477 * @option 478 * @type {boolean} 479 * @default true 480 */ 481 forceFollow: true 482 }; 483 484 // Window exports 485 Foundation.plugin(DropdownMenu, 'DropdownMenu'); 486}(jQuery);
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.