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