1/* 2 * Stellarnav.js 2.5.0 3 * Responsive, lightweight, multi-level dropdown menu. 4 * Copyright (c) 2018 Vinny Moreira - http://vinnymoreira.com 5 * Released under the MIT license 6 */ 7(function($) { 8 $.fn.stellarNav = function(options, width, breakpoint) { 9 10 var $nav, $width, $breakpoint, $parentItems; 11 nav = $(this); 12 width = $(window).width(); 13 14 // default settings 15 var settings = $.extend( { 16 theme: 'plain', // adds default color to nav. (light, dark) 17 breakpoint: 768, // number in pixels to determine when the nav should turn mobile friendly 18 menuLabel: '', // label for the mobile nav 19 sticky: false, // makes nav sticky on scroll (desktop only) 20 position: 'static', // 'static', 'top', 'left', 'right' - when set to 'top', this forces the mobile nav to be placed absolutely on the very top of page 21 openingSpeed: 250, // how fast the dropdown should open in milliseconds 22 closingDelay: 250, // controls how long the dropdowns stay open for in milliseconds 23 showArrows: true, // shows dropdown arrows next to the items that have sub menus 24 phoneBtn: '', // adds a click-to-call phone link to the top of menu - i.e.: "18009084500" 25 locationBtn: '', // adds a location link to the top of menu - i.e.: "/location/", "http://site.com/contact-us/" 26 closeBtn: false, // adds a close button to the end of nav 27 mobileMode: false, 28 scrollbarFix: false // fixes horizontal scrollbar issue on very long navs 29 }, options ); 30 31 return this.each( function() { 32 33 // defines black or white themes 34 if (settings.theme == 'light' || settings.theme == 'dark') { 35 nav.addClass(settings.theme); 36 } 37 38 if (settings.breakpoint) { 39 breakpoint = settings.breakpoint; 40 } 41 42 if (settings.menuLabel) { 43 menuLabel = settings.menuLabel; 44 } else { 45 menuLabel = ''; 46 } 47 48 // css classes for main menu mobile buttons 49 if( settings.phoneBtn && settings.locationBtn ) { 50 var cssClass = 'third'; 51 } else if( settings.phoneBtn || settings.locationBtn ) { 52 var cssClass = 'half'; 53 } else { 54 var cssClass = ''; 55 } 56 57 // adds the toggle button to open and close nav 58 if ( settings.position == 'right' || settings.position == 'left' ) { 59 nav.prepend('<a href="#" class="menu-toggle"><span class="bars"><span></span><span></span><span></span></span> ' + menuLabel + '</a>'); 60 } else { 61 nav.prepend('<a href="#" class="menu-toggle ' + cssClass + '"><span class="bars"><span></span><span></span><span></span></span> ' + menuLabel + '</a>'); 62 } 63 64 65 // Makes nav sticky on scroll 66 if (settings.sticky) { 67 navPos = nav.offset().top; 68 if(width >= breakpoint) { 69 $(window).bind('scroll', function() { 70 if ($(window).scrollTop() > navPos) { 71 nav.addClass('fixed'); 72 } 73 else { 74 nav.removeClass('fixed'); 75 } 76 }); 77 } 78 } 79 80 if (settings.position == 'top') { 81 nav.addClass('top'); 82 } 83 84 if (settings.position == 'left' || settings.position == 'right') { 85 var closeBtn = '<a href="#" class="close-menu ' + cssClass + '"><span class="icon-close"></span></a>'; 86 var phoneBtn = ''; 87 var locationBtn = ''; 88 89 nav.find('ul:first').prepend(closeBtn); 90 91 if (settings.locationBtn) { 92 nav.find('ul:first').prepend(locationBtn); 93 } 94 if (settings.phoneBtn) { 95 nav.find('ul:first').prepend(phoneBtn); 96 } 97 } 98 99 if (settings.position == 'right') { 100 nav.addClass('right'); 101 } 102 103 if (settings.position == 'left') { 104 nav.addClass('left'); 105 } 106 107 if (!settings.showArrows) { 108 nav.addClass('hide-arrows'); 109 } 110 111 if (settings.closeBtn && !(settings.position == 'right' || settings.position == 'left')) { 112 // adds a link to end of nav to close it 113 nav.find('ul:first').append('<li><a href="#" class="close-menu"><i class="fa fa-close"></i> Close Menu</a></li>'); 114 } 115 116 if (settings.scrollbarFix) { 117 $('body').addClass('stellarnav-noscroll-x'); 118 } 119 120 // svg graphics 121 // phone icon 122 var phoneIcon = document.getElementById('icon-phone'); // Gets phone icon element 123 if( phoneIcon ) { 124 phoneIcon.setAttribute('viewBox','0 0 480 480'); 125 var newPhoneIcon = document.createElementNS("http://www.w3.org/2000/svg", 'path'); // Create a path in SVG's namespace 126 newPhoneIcon.setAttribute('d','M340.273,275.083l-53.755-53.761c-10.707-10.664-28.438-10.34-39.518,0.744l-27.082,27.076 c-1.711-0.943-3.482-1.928-5.344-2.973c-17.102-9.476-40.509-22.464-65.14-47.113c-24.704-24.701-37.704-48.144-47.209-65.257 c-1.003-1.813
126-1.964-3.561-2.913-5.221l18.176-18.149l8.936-8.947c11.097-11.1,11.403-28.826,0.721-39.521L73.39,8.194 C62.708-2.486,44.969-2.162,33.872,8.938l-15.15,15.237l0.414,0.411c-5.08,6.482-9.325,13.958-12.484,22.02 C3.74,54.28,1.927,61.603,1.098,68.941C-6,127.785,20.89,181.564,93.866,254.541c100.875,100.868,182.167,93.248,185.674,92.876 c7.638-0.913,14.958-2.738,22.397-5.627c7.992-3.122,15.463-7.361,21.941-12.43l0.331,0.294l15.348-15.029 C350.631,303.527,350.95,285.795,340.273,275.083z'); // Sets path's data 127 phoneIcon.appendChild(newPhoneIcon); 128 } 129 130 // location icon 131 var locationIcon = document.getElementById('icon-location'); //Gets phone icon element 132 if( locationIcon ) { 133 locationIcon.setAttribute('viewBox','0 0 480 480'); 134 var newLocationIcon = document.createElementNS("http://www.w3.org/2000/svg", 'path'); // Create a path in SVG's namespace 135 newLocationIcon.setAttribute('d','M322.621,42.825C294.073,14.272,259.619,0,219.268,0c-40.353,0-74.803,14.275-103.353,42.825 c-28.549,28.549-42.825,63-42.825,103.353c0,20.749,3.14,37.782,9.419,51.106l104.21,220.986 c2.856,6.276,7.283,11.225,13.278,14.838c5.996,3.617,12.419,5.428,19.273,5.428c6.852,0,13.278-1.811,19.273-5.428 c5.996-3.613,10.513-8.562,13.559-14.838l103.918-220.986c6.282-13.324,9.424-30.358,9.424-51.106 C365.449,105.825,351.176,71.378,322.621,42.825z M270.942,197.855c-14.273,14.272-31.497,21.411-51.674,21.411 s-37.401-7.139-51.678-21.411c-14.275-14.277-21.414-31.501-21.414-51.678c0-20.175,7.139-37.402,21.414-51.675 c14.277-14.275,31.504-21.414,51.678-21.414c20.177,0,37.401,7.139,51.674,21.414c14.274,14.272,21.413,31.5,21.413,51.675 C292.355,166.352,285.217,183.575,270.942,197.855z'); // Sets path's data 136 locationIcon.appendChild(newLocationIcon); 137 } 138 139 // opens and closes menu 140 $('.menu-toggle, .stellarnav-open').on('click', function(e) { 141 e.preventDefault(); 142 143 // if nav position is left or right, uses fadeToggle instead of slideToggle 144 if (settings.position == 'left' || settings.position == 'right') { 145 nav.find('ul:first').stop(true, true).fadeToggle(settings.openingSpeed); 146 nav.toggleClass('active'); 147 148 if(nav.hasClass('active') && nav.hasClass('mobile')) { 149 // closes the menu when clicked outside of it 150 $(document).on('click', function(event) { 151 // ensures menu hides only on mobile nav 152 if(nav.hasClass('mobile')) { 153 if (!$(event.target).closest(nav).length) { 154 nav.find('ul:first').stop(true, true).fadeOut(settings.openingSpeed); 155 nav.removeClass('active'); 156 } 157 } 158 }); 159 } 160 161 } else { 162 // static position - normal open and close animation 163 nav.find('ul:first').stop(true, true).slideToggle(settings.openingSpeed); 164 nav.toggleClass('active'); 165 } 166 }); 167 168 // activates the close button 169 $('.close-menu, .stellarnav-close').on('click', function() { 170 171 nav.removeClass('active'); 172 173 if (settings.position == 'left' || settings.position == 'right') { 174 nav.find('ul:first').stop(true, true).fadeToggle(settings.openingSpeed); 175 } else { 176 nav.find('ul:first').stop(true, true).slideUp(settings.openingSpeed).toggleClass('active'); 177 } 178 }); 179 180 181 // adds toggle button to li items that have children 182 nav.find('li a').each(function() { 183 if ($(this).next().length > 0) { 184 $(this).parent('li').addClass('has-sub').append('<a class="dd-toggle" href="#"><span class="icon-plus"></span></a>'); 185 } 186 }); 187 188 // expands the dropdown menu on each click 189 nav.find('li .dd-toggle').on('click', function(e) { 190 e.preventDefault(); 191 $(this).parent('li').children('ul').stop(true, true).slideToggle(settings.openingSpeed); 192 $(this).parent('li').toggleClass('open'); 193 }); 194 195 var resetTriggers = function() { 196 nav.find('li').off('mouseenter'); 197 nav.find('li').off('mouseleave'); 198 } 199 200 // defines top level items 201 parentItems = nav.find('> ul > li'); 202 203 var setTriggers = function() { 204 $(parentItems).each(function() { 205 if( $(this).hasClass('mega') ) { 206 // mega dropdown 207 $(this).on('mouseenter', function(){ 208 $(this).find('ul').first().stop(true, true).slideDown(settings.openingSpeed); 209 }); 210 $(this).on('mouseleave', function(){
211 $(this).find('ul').first().stop(true, true).slideUp(settings.openingSpeed); 212 }); 213 } else { 214 // normal dropdown 215 // first-level 216 $(this).on('mouseenter', function(){ 217 $(this).children('ul').stop(true, true).slideDown(settings.openingSpeed); 218 }); 219 $(this).on('mouseleave', function(){ 220 $(this).children('ul').stop(true, true).delay(settings.closingDelay).slideUp(settings.openingSpeed); 221 }); 222 223 // second level and below 224 $(this).find('li.has-sub').on('mouseenter', function(){ 225 $(this).children('ul').stop(true, true).slideDown(settings.openingSpeed); 226 }); 227 $(this).find('li.has-sub').on('mouseleave', function(){ 228 $(this).children('ul').stop(true, true).delay(settings.closingDelay).slideUp(settings.openingSpeed); 229 }); 230 } 231 }); 232 } 233 234 windowCheck(); 235 236 // check browser width in real-time 237 function windowCheck() { 238 var browserWidth = window.innerWidth; 239 240 if(browserWidth <= breakpoint || settings.mobileMode) { 241 // mobile/tablet nav 242 243 resetTriggers(); 244 nav.addClass('mobile'); 245 nav.removeClass('desktop'); 246 247 // closes the menu when resizing window back to desktop 248 if( !nav.hasClass('active') && nav.find('ul:first').is(':visible') ) { 249 nav.find('ul:first').hide(); 250 } 251 252 // resets all the styles back to normal that are added on the desktop for the mega dropdown 253 nav.find('li.mega').each(function() { 254 $(this).find('ul').first().removeAttr('style'); 255 $(this).find('ul').first().children().removeAttr('style'); 256 }); 257 258 } else { 259 // desktop nav 260 nav.addClass('desktop'); 261 nav.removeClass('mobile'); 262 263 if(nav.hasClass('active')) { 264 nav.removeClass('active'); 265 } 266 267 // ensures stellarnav is visible after resizing window 268 if( !nav.hasClass('active') && nav.find('ul:first').is(':hidden') ) { 269 nav.find('ul:first').show(); 270 } 271 272 // hides items that were open on mobile 273 $('li.open').removeClass('open').find('ul:visible').hide(); 274 275 resetTriggers(); 276 setTriggers(); 277 278 // mega dropdown 279 navWidth = 0; 280 281 $(parentItems).each(function() { 282 // calculates the nav width based on the sum of all top-level items 283 navWidth += $(this)[0].getBoundingClientRect().width; 284 navWidth = Math.round(navWidth); 285 286 if($(this).hasClass('mega')) { 287 // left aligns mega dropdown with nav 288 $(this).find('ul').first().css({'left': 0, 'right': 0, 'margin': '0px auto'}); 289 290 // gets the data-column attribute and divides the columns equally 291 numCols = $(this).attr('data-columns'); 292 if(numCols==2) { 293 $(this).find('li.has-sub').width('50%'); 294 } else if (numCols==3) { 295 $(this).find('ul').first().children().width('33.33%'); 296 } else if (numCols==4) { 297 $(this).find('ul').first().children().width('25%'); 298 } else if (numCols==5) { 299 $(this).find('ul').first().children().width('20%'); 300 } else if (numCols==6) { 301 $(this).find('ul').first().children().width('16.66%'); 302 } else if (numCols==7) { 303 $(this).find('ul').first().children().width('14.28%'); 304 } else if (numCols==8) { 305 $(this).find('ul').first().children().width('12.5%'); 306 } else { 307 // defaults to 4 column 308 $(this).find('ul').first().children().width('25%'); 309 } 310 } 311 312 }); 313 314 // defines the mega dropdown width to be the same as nav width 315 if(parentItems.hasClass('mega')) { 316 nav.find('li.mega > ul').css({'max-width':navWidth}); 317 } 318 // end mega dropdown 319 320 } // end desktop nav 321 } // windowCheck() 322 323 $(window).on('resize', function() { 324 windowCheck(); 325 }); 326 }); 327 } 328}(jQuery));
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.