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