1/* 2 * Stellarnav.js 2.6.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: 'Menu', // 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 phoneLabel: 'Call Us', // label for the phone button 26 locationBtn: '', // adds a location link to the top of menu - i.e.: "/location/", "http://site.com/contact-us/" 27 locationLabel: 'Location', // label for the location button 28 closeBtn: false, // adds a close button to the end of nav 29 closeLabel: 'Close', // label for the close button 30 mobileMode: false, 31 scrollbarFix: false // fixes horizontal scrollbar issue on very long navs 32 }, options ); 33 34 return this.each( function() { 35 36 // defines black or white themes 37 if (settings.theme == 'light' || settings.theme == 'dark') { 38 nav.addClass(settings.theme); 39 } 40 41 if (settings.breakpoint) { 42 breakpoint = settings.breakpoint; 43 } 44 45 if (settings.menuLabel) { 46 menuLabel = settings.menuLabel; 47 } else { 48 menuLabel = ''; 49 } 50 51 if (settings.phoneLabel) { 52 phoneLabel = settings.phoneLabel; 53 } else { 54 phoneLabel = ''; 55 } 56 57 if (settings.locationLabel) { 58 locationLabel = settings.locationLabel; 59 } else { 60 locationLabel = ''; 61 } 62 63 if (settings.closeLabel) { 64 closeLabel = settings.closeLabel; 65 } else { 66 closeLabel = ''; 67 } 68 69 // css classes for main menu mobile buttons 70 if( settings.phoneBtn && settings.locationBtn ) { 71 var cssClass = 'third'; 72 } else if( settings.phoneBtn || settings.locationBtn ) { 73 var cssClass = 'half'; 74 } else { 75 var cssClass = 'full'; 76 } 77 78 // adds the toggle button to open and close nav 79 if ( settings.position == 'right' || settings.position == 'left' ) { 80 nav.prepend('<a href="#" class="menu-toggle"><span class="bars"><span></span><span></span><span></span></span> ' + menuLabel + '</a>'); 81 } else { 82 nav.prepend('<a href="#" class="menu-toggle ' + cssClass + '"><span class="bars"><span></span><span></span><span></span></span> ' + menuLabel + '</a>'); 83 } 84 85 // adds a click-to-call link 86 if (settings.phoneBtn && !(settings.position == 'right' || settings.position == 'left')) { 87 var btn = '<a href="tel:'+ settings.phoneBtn +'" class="call-btn-mobile ' + cssClass + '"><svg id="icon-phone"></svg> <span>' + phoneLabel + '</span></a>'; 88 nav.find('a.menu-toggle').after(btn); 89 } 90 91 // adds a location page link to the beginning of nav 92 if (settings.locationBtn && !(settings.position == 'right' || settings.position == 'left')) { 93 var btn = '<a href="'+ settings.locationBtn +'" class="location-btn-mobile ' + cssClass + '" target="_blank"><svg id="icon-location"></svg> <span>' + locationLabel + '</span></a>'; 94 nav.find('a.menu-toggle').after(btn); 95 } 96 97 // Makes nav sticky on scroll 98 if (settings.sticky) { 99 navPos = nav.offset().top; 100 if(width >= breakpoint) { 101 $(window).on('scroll', function() { 102 if ($(window).scrollTop() > navPos) { 103 nav.addClass('fixed'); 104 } 105 else { 106 nav.removeClass('fixed'); 107 } 108 }); 109 } 110 } 111 112 if (settings.position == 'top') { 113 nav.addClass('top'); 114 } 115 116 if (settings.position == 'left' || settings.position == 'right') { 117 var closeBtn = '<a href="#" class="close-menu ' + cssClass + '"><span class="icon-close"></span>' + closeLabel + '</a>'; 118 var phoneBtn = '<a href="tel:'+ settings.phoneBtn +'" class="call-btn-mobile ' + cssClass + '"><svg id="icon-phone"></svg></a>'; 119 var locationBtn = '<a href="'+ settings.locationBtn +'" class="location-btn-mobile ' + cssClass + '" target="_blank"><svg id="icon-location"></svg></i></a>'; 120 121 nav.find('ul:first').prepend(closeBtn); 122 123 if (settings.locationBtn) { 124 nav.find('ul:first').prepend(locationBtn); 125 } 126 if (settings.phoneBtn) { 127 nav.find('ul:first').prepend(phoneBtn); 128 } 129 } 130 131 if (settings.position == 'right') { 132 nav.addClass('right'); 133 } 134 135 if (settings.position == 'left') { 136 nav.addClass('left'); 137 } 138 139 if (!settings.showArrows) { 140 nav.addClass('hide-arrows'); 141 } 142 143 if (settings.closeBtn && !(settings.position == 'right' || settings.position == 'left')) { 144 // adds a link to end of nav to close it 145 nav.find('ul:first').append('<li><a href="#" class="close-menu"><span class="icon-close"></span> ' + closeLabel + '</a></li>'); 146 } 147 148 if (settings.scrollbarFix) { 149 $('body').addClass('stellarnav-noscroll-x'); 150 } 151 152 // svg graphics 153 // phone icon 154 var phoneIcon = document.getElementById('icon-phone'); // Gets phone icon element 155 if( phoneIcon ) { 156 phoneIcon.setAttribute('viewBox','0 0 480 480'); 157 var newPhoneIcon = document.createElementNS("http://www.w3.org/2000/svg", 'path'); // Create a path in SVG's namespace 158 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
158-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 159 phoneIcon.appendChild(newPhoneIcon); 160 } 161 162 // location icon 163 var locationIcon = document.getElementById('icon-location'); //Gets phone icon element 164 if( locationIcon ) { 165 locationIcon.setAttribute('viewBox','0 0 480 480'); 166 var newLocationIcon = document.createElementNS("http://www.w3.org/2000/svg", 'path'); // Create a path in SVG's namespace 167 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 168 locationIcon.appendChild(newLocationIcon); 169 } 170 171 // opens and closes menu 172 $('.menu-toggle, .stellarnav-open').on('click', function(e) { 173 e.preventDefault(); 174 175 // if nav position is left or right, uses fadeToggle instead of slideToggle 176 if (settings.position == 'left' || settings.position == 'right') { 177 nav.find('ul:first').stop(true, true).fadeToggle(settings.openingSpeed); 178 nav.toggleClass('active'); 179 180 if(nav.hasClass('active') && nav.hasClass('mobile')) { 181 // closes the menu when clicked outside of it 182 $(document).on('click', function(event) { 183 // ensures menu hides only on mobile nav 184 if(nav.hasClass('mobile')) { 185 if (!$(event.target).closest(nav).length) { 186 nav.find('ul:first').stop(true, true).fadeOut(settings.openingSpeed); 187 nav.removeClass('active'); 188 } 189 } 190 }); 191 } 192 193 } else { 194 // static position - normal open and close animation 195 nav.find('ul:first').stop(true, true).slideToggle(settings.openingSpeed); 196 nav.toggleClass('active'); 197 } 198 }); 199 200 // activates the close button 201 $('.close-menu, .stellarnav-close').on('click', function() { 202 203 nav.removeClass('active'); 204 205 if (settings.position == 'left' || settings.position == 'right') { 206 nav.find('ul:first').stop(true, true).fadeToggle(settings.openingSpeed); 207 } else { 208 nav.find('ul:first').stop(true, true).slideUp(settings.openingSpeed).toggleClass('active'); 209 } 210 }); 211 212 213 // adds toggle button to li items that have children 214 nav.find('li a').each(function() { 215 if ($(this).next().length > 0) { 216 $(this).parent('li').addClass('has-sub').append('<a class="dd-toggle" href="#"><span class="icon-plus"></span></a>'); 217 } 218 }); 219 220 221 222 // expands the dropdown menu on each click 223 nav.find('li .dd-title, li .dd-toggle').on('click', function(e) { 224 e.preventDefault(); 225 $(this).parent('li').children('ul').stop(true, true).slideToggle(settings.openingSpeed); 226 $(this).parent('li').toggleClass('open'); 227 }); 228 229 var resetTriggers = function() { 230 nav.find('li').off('mouseenter'); 231 nav.find('li').off('mouseleave'); 232 } 233 234 // defines top level items 235 parentItems = nav.find('> ul > li'); 236 237 var setTriggers = function() { 238 $(parentItems).each(function() { 239 if( $(this).hasClass('mega') ) { 240 // mega dropdown 241 $(this).on('mouseenter', function(){ 242 $(this).find('ul').first().stop(true, true).slideDown(settings.openingSpeed); 243 }); 244 $(this).on('mouseleave', function(){
245 $(this).find('ul').first().stop(true, true).slideUp(settings.openingSpeed); 246 }); 247 } else { 248 // normal dropdown 249 // first-level 250 $(this).on('mouseenter', function(){ 251 $(this).children('ul').stop(true, true).slideDown(settings.openingSpeed); 252 }); 253 $(this).on('mouseleave', function(){ 254 $(this).children('ul').stop(true, true).delay(settings.closingDelay).slideUp(settings.openingSpeed); 255 }); 256 257 // second level and below 258 $(this).find('li.has-sub').on('mouseenter', function(){ 259 $(this).children('ul').stop(true, true).slideDown(settings.openingSpeed); 260 }); 261 $(this).find('li.has-sub').on('mouseleave', function(){ 262 $(this).children('ul').stop(true, true).delay(settings.closingDelay).slideUp(settings.openingSpeed); 263 }); 264 } 265 }); 266 } 267 268 windowCheck(); 269 270 // check browser width in real-time 271 function windowCheck() { 272 var browserWidth = window.innerWidth; 273 274 if(browserWidth <= breakpoint || settings.mobileMode) { 275 // mobile/tablet nav 276 277 resetTriggers(); 278 nav.addClass('mobile'); 279 nav.removeClass('desktop'); 280 281 // closes the menu when resizing window back to desktop 282 if( !nav.hasClass('active') && nav.find('ul:first').is(':visible') ) { 283 nav.find('ul:first').hide(); 284 } 285 286 // resets all the styles back to normal that are added on the desktop for the mega dropdown 287 nav.find('li.mega').each(function() { 288 $(this).find('ul').first().removeAttr('style'); 289 $(this).find('ul').first().children().removeAttr('style'); 290 }); 291 292 } else { 293 // desktop nav 294 nav.addClass('desktop'); 295 nav.removeClass('mobile'); 296 297 if(nav.hasClass('active')) { 298 nav.removeClass('active'); 299 } 300 301 // ensures stellarnav is visible after resizing window 302 if( !nav.hasClass('active') && nav.find('ul:first').is(':hidden') ) { 303 nav.find('ul:first').show(); 304 } 305 306 // hides items that were open on mobile 307 $('li.open').removeClass('open').find('ul:visible').hide(); 308 309 resetTriggers(); 310 setTriggers(); 311 312 // mega dropdown 313 navWidth = 0; 314 315 $(parentItems).each(function() { 316 // calculates the nav width based on the sum of all top-level items 317 navWidth += $(this)[0].getBoundingClientRect().width; 318 navWidth = Math.round(navWidth); 319 320 if($(this).hasClass('mega')) { 321 // left aligns mega dropdown with nav 322 $(this).find('ul').first().css({'left': 0, 'right': 0, 'margin': '0px auto'}); 323 324 // gets the data-column attribute and divides the columns equally 325 numCols = $(this).attr('data-columns'); 326 if(numCols==2) { 327 $(this).find('li.has-sub').width('50%'); 328 } else if (numCols==3) { 329 $(this).find('ul').first().children().width('33.33%'); 330 } else if (numCols==4) { 331 $(this).find('ul').first().children().width('25%'); 332 } else if (numCols==5) { 333 $(this).find('ul').first().children().width('20%'); 334 } else if (numCols==6) { 335 $(this).find('ul').first().children().width('16.66%'); 336 } else if (numCols==7) { 337 $(this).find('ul').first().children().width('14.28%'); 338 } else if (numCols==8) { 339 $(this).find('ul').first().children().width('12.5%'); 340 } else { 341 // defaults to 4 column 342 $(this).find('ul').first().children().width('25%'); 343 } 344 } 345 346 }); 347 348 // defines the mega dropdown width to be the same as nav width 349 if(parentItems.hasClass('mega')) { 350 nav.find('li.mega > ul').css({'max-width':navWidth}); 351 } 352 // end mega dropdown 353 354 } // end desktop nav 355 } // windowCheck() 356 357 $(window).on('resize', function() { 358 windowCheck(); 359 }); 360 }); 361 } 362}(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.