vendor: 5,308 bytes, lines 1-166
1/*! 2 * SlickNav Responsive Mobile Menu v1.0.10 3 * (c) 2016 Josh Cope 4 * licensed under MIT 5 */ 6;(function ($, document, window) { 7 var 8 // default settings object. 9 defaults = { 10 label: 'MENU', 11 duplicate: true, 12 duration: 200, 13 easingOpen: 'swing', 14 easingClose: 'swing', 15 closedSymbol: '►', 16 openedSymbol: '▼', 17 prependTo: 'body', 18 appendTo: '', 19 parentTag: 'a', 20 closeOnClick: false, 21 allowParentLinks: false, 22 nestedParentLinks: true, 23 showChildren: false, 24 removeIds: true, 25 removeClasses: false, 26 removeStyles: false, 27 brand: '', 28 animations: 'jquery', 29 init: function () {}, 30 beforeOpen: function () {}, 31 beforeClose: function () {}, 32 afterOpen: function () {}, 33 afterClose: function () {} 34 }, 35 mobileMenu = 'slicknav', 36 prefix = 'slicknav'; 37 38 Keyboard = { 39 DOWN: 40, 40 ENTER: 13, 41 ESCAPE: 27, 42 LEFT: 37, 43 RIGHT: 39, 44 SPACE: 32, 45 TAB: 9, 46 UP: 38, 47 }; 48 49 function Plugin(element, options) { 50 this.element = element; 51 52 // jQuery has an extend method which merges the contents of two or 53 // more objects, storing the result in the first object. The first object 54 // is generally empty as we don't want to alter the default options for 55 // future instances of the plugin 56 this.settings = $.extend({}, defaults, options); 57 58 // Don't remove IDs by default if duplicate is false 59 if (!this.settings.duplicate && !options.hasOwnProperty("removeIds")) { 60 this.settings.removeIds = false; 61 } 62 63 this._defaults = defaults; 64 this._name = mobileMenu; 65 66 this.init(); 67 } 68 69 Plugin.prototype.init = function () { 70 var $this = this, 71 menu = $(this.element), 72 settings = this.settings, 73 iconClass, 74 menuBar; 75 76 // clone menu if needed 77 if (settings.duplicate) { 78 $this.mobileNav = menu.clone(); 79 } else { 80 $this.mobileNav = menu; 81 } 82 83 // remove IDs if set 84 if (settings.removeIds) { 85 $this.mobileNav.removeAttr('id'); 86 $this.mobileNav.find('*').each(function (i, e) { 87 $(e).removeAttr('id'); 88 }); 89 } 90 91 // remove classes if set 92 if (settings.removeClasses) { 93 $this.mobileNav.removeAttr('class'); 94 $this.mobileNav.find('*').each(function (i, e) { 95 $(e).removeAttr('class'); 96 }); 97 } 98 99 // remove styles if set 100 if (settings.removeStyles) { 101 $this.mobileNav.removeAttr('style'); 102 $this.mobileNav.find('*').each(function (i, e) { 103 $(e).removeAttr('style'); 104 }); 105 } 106 107 // styling class for the button 108 iconClass = prefix + '_icon'; 109 110 if (settings.label === '') { 111 iconClass += ' ' + prefix + '_no-text'; 112 } 113 114 if (settings.parentTag === 'a') { 115 settings.parentTag = 'a href="#"'; 116 } 117 118 // create menu bar 119 $this.mobileNav.attr('class', prefix + '_nav'); 120 menuBar = $('<div class="' + prefix + '_menu"></div>'); 121 if (settings.brand !== '') { 122 var brand = $('<div class="' + prefix + '_brand">'+settings.brand+'</div>'); 123 $(menuBar).append(brand); 124 } 125 $this.btn = $( 126 ['<' + settings.parentTag + ' aria-haspopup="true" role="button" tabindex="0" class="' + prefix + '_btn ' + prefix + '_collapsed">', 127 '<span class="' + prefix + '_menutxt">' + settings.label + '</span>', 128 '<span class="' + iconClass + '">', 129 '<span class="' + prefix + '_icon-bar"></span>', 130 '<span class="' + prefix + '_icon-bar"></span>', 131 '<span class="' + prefix + '_icon-bar"></span>', 132 '</span>', 133 '</' + settings.parentTag + '>' 134 ].join('') 135 ); 136 $('.navbar-toggle').append($this.btn); 137 if(settings.appendTo !== '') { 138 $(settings.appendTo).append(menuBar); 139 } else { 140 $(settings.prependTo).prepend(menuBar); 141 } 142 menuBar.append($this.mobileNav); 143 144 // iterate over structure adding additional structure 145 var items = $this.mobileNav.find('li'); 146 $(items).each(function () { 147 var item = $(this), 148 data = {}; 149 data.children = item.children('ul').attr('role', 'menu'); 150 item.data('menu', data); 151 152 // if a list item has a nested menu 153 if (data.children.length > 0) { 154 155 // select all text before the child menu 156 // check for anchors 157 158 var a = item.contents(), 159 containsAnchor = false, 160 nodes = []; 161 162 $(a).each(function () { 163 if (!$(this).is('ul')) { 164 nodes.push(this); 165 } else { 166 return false;
167 } 168 169 if($(this).is("a")) { 170 containsAnchor = true; 171 } 172 }); 173 174 var wrapElement = $( 175 '<' + settings.parentTag + ' role="menuitem" aria-haspopup="true" tabindex="-1" class="' + prefix + '_item"/>' 176 ); 177 178 // wrap item text with tag and add classes unless we are separating parent links 179 if ((!settings.allowParentLinks || settings.nestedParentLinks) || !containsAnchor) { 180 var $wrap = $(nodes).wrapAll(wrapElement).parent(); 181 $wrap.addClass(prefix+'_row'); 182 } else 183 $(nodes).wrapAll('<span class="'+prefix+'_parent-link '+prefix+'_row"/>').parent(); 184 185 if (!settings.showChildren) { 186 item.addClass(prefix+'_collapsed'); 187 } else { 188 item.addClass(prefix+'_open'); 189 } 190 191 item.addClass(prefix+'_parent'); 192 193 // create parent arrow. wrap with link if parent links and separating 194 var arrowElement = $('<span class="'+prefix+'_arrow">'+(settings.showChildren?settings.openedSymbol:settings.closedSymbol)+'</span>'); 195 196 if (settings.allowParentLinks && !settings.nestedParentLinks && containsAnchor) 197 arrowElement = arrowElement.wrap(wrapElement).parent(); 198 199 //append arrow 200 $(nodes).last().after(arrowElement); 201 202 203 } else if ( item.children().length === 0) { 204 item.addClass(prefix+'_txtnode'); 205 } 206 207 // accessibility for links 208 item.children('a').attr('role', 'menuitem').on("click", function(event){ 209 //Ensure that it's not a parent 210 if (settings.closeOnClick && !$(event.target).parent().closest('li').hasClass(prefix+'_parent')) { 211 //Emulate menu close if set 212 $($this.btn).trigger('click'); 213 } 214 }); 215 216 //also close on click if parent links are set 217 if (settings.closeOnClick && settings.allowParentLinks) { 218 219 item.children('a').children('a').on("click", function(event){ 220 //Emulate menu close 221 $($this.btn).trigger('click'); 222 }); 223 224 item.find('.'+prefix+'_parent-link a:not(.'+prefix+'_item)').on("click", function(event){ 225 //Emulate menu close 226 $($this.btn).trigger('click'); 227 }); 228 } 229 }); 230 231 // structure is in place, now hide appropriate items 232 $(items).each(function () { 233 var data = $(this).data('menu'); 234 if (!settings.showChildren){ 235 $this._visibilityToggle(data.children, null, false, null, true); 236 } 237 }); 238 239 // finally toggle entire menu 240 $this._visibilityToggle($this.mobileNav, null, false, 'init', true); 241 242 // accessibility for menu button 243 $this.mobileNav.attr('role','menu'); 244 245 // outline prevention when using mouse 246 $(document).on( "mousedown", function() { 247 $this._outlines(false); 248 }); 249 250 $(document).on( "keyup", function() { 251 $this._outlines(true); 252 }); 253 254 // menu button click 255 $($this.btn).on("click", function(e){ 256 e.preventDefault(); 257 $this._menuToggle(); 258 }); 259 260 // click on menu parent 261 $this.mobileNav.on('click', '.' + prefix + '_item', function (e) { 262 e.preventDefault(); 263 $this._itemClick($(this)); 264 }); 265 266 // check for keyboard events on menu button and menu parents 267 $($this.btn).on("keydown", function(e){ 268 var ev = e || event; 269 270 switch(ev.keyCode) { 271 case Keyboard.ENTER: 272 case Keyboard.SPACE: 273 case Keyboard.DOWN: 274 e.preventDefault(); 275 if (ev.keyCode !== Keyboard.DOWN || !$($this.btn).hasClass(prefix+'_open')){ 276 $this._menuToggle(); 277 } 278 279 $($this.btn).next().find('[role="menuitem"]').first().trigger( "focus" ); 280 break; 281 } 282 283 284 }); 285 286 $this.mobileNav.on('keydown', '.'+prefix+'_item', function(e) { 287 var ev = e || event; 288 289 switch(ev.keyCode) { 290 case Keyboard.ENTER: 291 e.preventDefault(); 292 $this._itemClick($(e.target)); 293 break; 294 case Keyboard.RIGHT: 295 e.preventDefault(); 296 if ($(e.target).parent().hasClass(prefix+'_collapsed')) { 297 $this._itemClick($(e.target)); 298 } 299 $(e.target).next().find('[role="menuitem"]').first().trigger( "focus" ); 300 break; 301 } 302 }); 303 304 $this.mobileNav.on('keydown', '[role="menuitem"]', function(e) { 305 var ev = e || event; 306 307 switch(ev.keyCode){
308 case Keyboard.DOWN: 309 e.preventDefault(); 310 var allItems = $(e.target).parent().parent().children().children('[role="menuitem"]:visible'); 311 var idx = allItems.index( e.target ); 312 var nextIdx = idx + 1; 313 if (allItems.length <= nextIdx) { 314 nextIdx = 0; 315 } 316 var next = allItems.eq( nextIdx ); 317 next.trigger( "focus" ); 318 break; 319 case Keyboard.UP: 320 e.preventDefault(); 321 var allItems = $(e.target).parent().parent().children().children('[role="menuitem"]:visible'); 322 var idx = allItems.index( e.target ); 323 var next = allItems.eq( idx - 1 ); 324 next.trigger( "focus" ); 325 break; 326 case Keyboard.LEFT: 327 e.preventDefault(); 328 if ($(e.target).parent().parent().parent().hasClass(prefix+'_open')) { 329 var parent = $(e.target).parent().parent().prev(); 330 parent.trigger( "focus" ); 331 $this._itemClick(parent); 332 } else if ($(e.target).parent().parent().hasClass(prefix+'_nav')){ 333 $this._menuToggle(); 334 $($this.btn).trigger( "focus" ); 335 } 336 break; 337 case Keyboard.ESCAPE: 338 e.preventDefault(); 339 $this._menuToggle(); 340 $($this.btn).trigger( "focus" ); 341 break; 342 } 343 }); 344 345 // allow links clickable within parent tags if set 346 if (settings.allowParentLinks && settings.nestedParentLinks) { 347 $('.'+prefix+'_item a').on("click", function(e){ 348 e.stopImmediatePropagation(); 349 }); 350 } 351 }; 352 353 //toggle menu 354 Plugin.prototype._menuToggle = function (el) { 355 var $this = this; 356 var btn = $this.btn; 357 var mobileNav = $this.mobileNav; 358 359 if (btn.hasClass(prefix+'_collapsed')) { 360 btn.removeClass(prefix+'_collapsed'); 361 btn.addClass(prefix+'_open'); 362 } else { 363 btn.removeClass(prefix+'_open'); 364 btn.addClass(prefix+'_collapsed'); 365 } 366 btn.addClass(prefix+'_animating'); 367 $this._visibilityToggle(mobileNav, btn.parent(), true, btn); 368 }; 369 370 // toggle clicked items 371 Plugin.prototype._itemClick = function (el) { 372 var $this = this; 373 var settings = $this.settings; 374 var data = el.data('menu'); 375 if (!data) { 376 data = {}; 377 data.arrow = el.children('.'+prefix+'_arrow'); 378 data.ul = el.next('ul'); 379 data.parent = el.parent(); 380 //Separated parent link structure 381 if (data.parent.hasClass(prefix+'_parent-link')) { 382 data.parent = el.parent().parent(); 383 data.ul = el.parent().next('ul'); 384 } 385 el.data('menu', data); 386 } 387 if (data.parent.hasClass(prefix+'_collapsed')) { 388 data.arrow.html(settings.openedSymbol); 389 data.parent.removeClass(prefix+'_collapsed'); 390 data.parent.addClass(prefix+'_open'); 391 data.parent.addClass(prefix+'_animating'); 392 $this._visibilityToggle(data.ul, data.parent, true, el); 393 } else { 394 data.arrow.html(settings.closedSymbol); 395 data.parent.addClass(prefix+'_collapsed'); 396 data.parent.removeClass(prefix+'_open'); 397 data.parent.addClass(prefix+'_animating'); 398 $this._visibilityToggle(data.ul, data.parent, true, el); 399 } 400 }; 401 402 // toggle actual visibility and accessibility tags 403 Plugin.prototype._visibilityToggle = function(el, parent, animate, trigger, init) { 404 var $this = this; 405 var settings = $this.settings; 406 var items = $this._getActionItems(el); 407 var duration = 0; 408 if (animate) { 409 duration = settings.duration; 410 } 411 412 function afterOpen(trigger, parent) { 413 $(trigger).removeClass(prefix+'_animating'); 414 $(parent).removeClass(prefix+'_animating'); 415 416 //Fire afterOpen callback 417 if (!init) { 418 settings.afterOpen(trigger); 419 } 420 } 421
vendor: 6,211 bytes, lines 422-590
422 function afterClose(trigger, parent) { 423 el.attr('aria-hidden','true'); 424 items.attr('tabindex', '-1'); 425 $this._setVisAttr(el, true); 426 el.hide(); //jQuery 1.7 bug fix 427 428 $(trigger).removeClass(prefix+'_animating'); 429 $(parent).removeClass(prefix+'_animating'); 430 431 //Fire init or afterClose callback 432 if (!init){ 433 settings.afterClose(trigger); 434 } else if (trigger === 'init'){ 435 settings.init(); 436 } 437 } 438 439 if (el.hasClass(prefix+'_hidden')) { 440 el.removeClass(prefix+'_hidden'); 441 //Fire beforeOpen callback 442 if (!init) { 443 settings.beforeOpen(trigger); 444 } 445 if (settings.animations === 'jquery') { 446 el.stop(true,true).slideDown(duration, settings.easingOpen, function(){ 447 afterOpen(trigger, parent); 448 }); 449 } else if(settings.animations === 'velocity') { 450 el.velocity("finish").velocity("slideDown", { 451 duration: duration, 452 easing: settings.easingOpen, 453 complete: function() { 454 afterOpen(trigger, parent); 455 } 456 }); 457 } 458 el.attr('aria-hidden','false'); 459 items.attr('tabindex', '0'); 460 $this._setVisAttr(el, false); 461 } else { 462 el.addClass(prefix+'_hidden'); 463 464 //Fire init or beforeClose callback 465 if (!init){ 466 settings.beforeClose(trigger); 467 } 468 469 if (settings.animations === 'jquery') { 470 el.stop(true,true).slideUp(duration, this.settings.easingClose, function() { 471 afterClose(trigger, parent) 472 }); 473 } else if (settings.animations === 'velocity') { 474 475 el.velocity("finish").velocity("slideUp", { 476 duration: duration, 477 easing: settings.easingClose, 478 complete: function() { 479 afterClose(trigger, parent); 480 } 481 }); 482 } 483 } 484 }; 485 486 // set attributes of element and children based on visibility 487 Plugin.prototype._setVisAttr = function(el, hidden) { 488 var $this = this; 489 490 // select all parents that aren't hidden 491 var nonHidden = el.children('li').children('ul').not('.'+prefix+'_hidden'); 492 493 // iterate over all items setting appropriate tags 494 if (!hidden) { 495 nonHidden.each(function(){ 496 var ul = $(this); 497 ul.attr('aria-hidden','false'); 498 var items = $this._getActionItems(ul); 499 items.attr('tabindex', '0'); 500 $this._setVisAttr(ul, hidden); 501 }); 502 } else { 503 nonHidden.each(function(){ 504 var ul = $(this); 505 ul.attr('aria-hidden','true'); 506 var items = $this._getActionItems(ul); 507 items.attr('tabindex', '-1'); 508 $this._setVisAttr(ul, hidden); 509 }); 510 } 511 }; 512 513 // get all 1st level items that are clickable 514 Plugin.prototype._getActionItems = function(el) { 515 var data = el.data("menu"); 516 if (!data) { 517 data = {}; 518 var items = el.children('li'); 519 var anchors = items.find('a'); 520 data.links = anchors.add(items.find('.'+prefix+'_item')); 521 el.data('menu', data); 522 } 523 return data.links; 524 }; 525 526 Plugin.prototype._outlines = function(state) { 527 if (!state) { 528 $('.'+prefix+'_item, .'+prefix+'_btn').css('outline','none'); 529 } else { 530 $('.'+prefix+'_item, .'+prefix+'_btn').css('outline',''); 531 } 532 }; 533 534 Plugin.prototype.toggle = function(){ 535 var $this = this; 536 $this._menuToggle(); 537 }; 538 539 Plugin.prototype.open = function(){ 540 var $this = this; 541 if ($this.btn.hasClass(prefix+'_collapsed')) { 542 $this._menuToggle(); 543 } 544 }; 545 546 Plugin.prototype.close = function(){ 547 var $this = this; 548 if ($this.btn.hasClass(prefix+'_open')) { 549 $this._menuToggle(); 550 } 551 }; 552 553 $.fn[mobileMenu] = function ( options ) { 554 var args = arguments; 555 556 // Is the first parameter an object (options), or was omitted, instantiate a new instance 557 if (options === undefined || typeof options === 'object') { 558 return this.each(function () { 559 560 // Only allow the plugin to be instantiated once due to methods 561 if (!$.data(this, 'plugin_' + mobileMenu)) { 562 563 // if it has no instance, create a new one, pass options to our plugin constructor, 564 // and store the plugin instance in the elements jQuery data object. 565 $.data(this, 'plugin_' + mobileMenu, new Plugin( this, options )); 566 } 567 }); 568 569 // If is a string and doesn't start with an underscore or 'init' function, treat this as a call to a public method. 570 } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') { 571 572 // Cache the method call to make it possible to return a value 573 var returns; 574 575 this.each(function () { 576 var instance = $.data(this, 'plugin_' + mobileMenu); 577 578 // Tests that there's already a plugin-instance and checks that the requested public method exists 579 if (instance instanceof Plugin && typeof instance[options] === 'function') { 580 581 // Call the method of our plugin instance, and pass it the supplied arguments. 582 returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) ); 583 } 584 }); 585 586 // If the earlier cached method gives a value back return the value, otherwise return this to preserve chainability. 587 return returns !== undefined ? returns : this; 588 } 589 }; 590}(jQuery, document, window));
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.