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