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