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