1;(function(window) { 2 3 'use strict'; 4 5 var support = { animations : Modernizr.cssanimations }, 6 animEndEventNames = { 'WebkitAnimation' : 'webkitAnimationEnd', 'OAnimation' : 'oAnimationEnd', 'msAnimation' : 'MSAnimationEnd', 'animation' : 'animationend' }, 7 animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ], 8 onEndAnimation = function( el, callback ) { 9 var onEndCallbackFn = function( ev ) { 10 if( support.animations ) { 11 if( ev.target != this ) return; 12 this.removeEventListener( animEndEventName, onEndCallbackFn ); 13 } 14 if( callback && typeof callback === 'function' ) { callback.call(); } 15 }; 16 if( support.animations ) { 17 el.addEventListener( animEndEventName, onEndCallbackFn ); 18 } 19 else { 20 onEndCallbackFn(); 21 } 22 }; 23 24 function extend( a, b ) { 25 for( var key in b ) { 26 if( b.hasOwnProperty( key ) ) { 27 a[key] = b[key]; 28 } 29 } 30 return a; 31 } 32 33 function MLMenu(el, options) { 34 this.el = el; 35 this.options = extend( {}, this.options ); 36 extend( this.options, options ); 37 38 // the menus (<ul>´s) 39 this.menus = [].slice.call(this.el.querySelectorAll('.menu__level')); 40 41 // index of current menu 42 // Each level is actually a different menu so 0 is root, 1 is sub-1, 2 sub-2, etc. 43 this.current_menu = 0; 44 45 /* Determine what current menu actually is */ 46 var current_menu; 47 this.menus.forEach(function(menuEl, pos) { 48 var items = menuEl.querySelectorAll('.menu__item'); 49 items.forEach(function(itemEl, iPos) { 50 var currentLink = itemEl.querySelector('.menu__link--current'); 51 if (currentLink) { 52 // This is the actual menu__level that should have current 53 current_menu = pos; 54 } 55 }); 56 }); 57 58 if (current_menu) { 59 this.current_menu = current_menu; 60 } 61 62 this._init(); 63 } 64 65 MLMenu.prototype.options = { 66 // show breadcrumbs 67 breadcrumbsCtrl : true, 68 // initial breadcrumb text 69 initialBreadcrumb : home_landing_txt, 70 // show back button 71 backCtrl : true, 72 // delay between each menu item sliding animation 73 itemsDelayInterval : 60, 74 // direction 75 direction : 'r2l', 76 // callback: item that doesn´t have a submenu gets clicked 77 // onItemClick([event], [inner HTML of the clicked item]) 78 onItemClick : function(ev, itemName) { return false; } 79 }; 80 81 MLMenu.prototype._init = function() { 82 // iterate the existing menus and create an array of menus, 83 // more specifically an array of objects where each one holds the info of each menu element and its menu items 84 this.menusArr = []; 85 this.breadCrumbs = false; 86 var self = this; 87 var submenus = []; 88 89 /* Loops over root level menu items */ 90 this.menus.forEach(function(menuEl, pos) { 91 var menu = {menuEl : menuEl, menuItems : [].slice.call(menuEl.querySelectorAll('.menu__item'))}; 92 93 self.menusArr.push(menu); 94 95 // set current menu class 96 if( pos === self.current_menu ) { 97 classie.add(menuEl, 'menu__level--current'); 98 } 99 100 var menu_x = menuEl.getAttribute('data-menu'); 101 var links = menuEl.querySelectorAll('.menu__link'); 102 links.forEach(function(linkEl, lPos) { 103 var submenu = linkEl.getAttribute('data-submenu'); 104 if (submenu) { 105 var pushMe = {"menu":submenu, "name": linkEl.innerHTML }; 106 if (submenus[pos]) { 107 submenus[pos].push(pushMe); 108 } else { 109 submenus[pos] = [] 110 submenus[pos].push(pushMe); 111 } 112 } 113 }); 114 }); 115 116 /* For each MENU, find their parent MENU */ 117 this.menus.forEach(function(menuEl, pos) { 118 var menu_x = menuEl.getAttribute('data-menu'); 119 submenus.forEach(function(subMenuEl, menu_root) { 120 subMenuEl.forEach(function(subMenuItem, subPos) { 121 if (subMenuItem.menu == menu_x) { 122 self.menusArr[pos].backIdx = menu_root; 123 self.menusArr[pos].name = subMenuItem.name; 124 } 125 }); 126 }); 127 }); 128 129 // create breadcrumbs 130 if( self.options.breadcrumbsCtrl ) { 131 this.breadcrumbsCtrl = document.createElement('nav'); 132 this.breadcrumbsCtrl.className = 'menu__breadcrumbs noBack'; 133 this.breadcrumbsCtrl.setAttribute('aria-label', 'You are here'); 134 this.el.insertBefore(this.breadcrumbsCtrl, this.el.firstChild); 135 // add initial breadcrumb 136 this._addBreadcrumb(0); 137 138 // Need to add breadcrumbs for all parents of current submenu 139 if (self.menusArr[self.current_menu].backIdx != 0 && self.current_menu != 0) { 140 this._crawlCrumbs(self.menusArr[self.current_menu].backIdx, self.menusArr); 141 this.breadCrumbs = true; 142 } 143 144 // Create current submenu breadcrumb 145 if (self.current_menu != 0) { 146 this._addBreadcrumb(self.current_menu); 147 this.breadCrumbs = true; 148 } 149 } 150 151 // create back button 152 this.options.backCtrl = 1; 153 if (this.options.backCtrl) { 154 155 this.backCtrl = document.createElement('button'); 156 if (this.breadCrumbs) { 157 this.backCtrl.className = 'menu__back'; 158 } else { 159 this.backCtrl.className = 'menu__back menu__back--hidden'; 160 } 161 this.backCtrl.setAttribute('aria-label', 'Go back'); 162 this.backCtrl.innerHTML = '<span class="icon icon--arrow-left"></span>'; 163 this.el.insertBefore(this.backCtrl, this.el.firstChild); 164 } 165 166 // event binding 167 this._initEvents(); 168 }; 169 170 MLMenu.prototype._initEvents = function() { 171 var self = this; 172 173 for(var i = 0, len = this.menusArr.length; i < len; ++i) {
174 this.menusArr[i].menuItems.forEach(function(item, pos) { 175 item.querySelector('a').addEventListener('click', function(ev) { 176 177 var submenu = ev.target.getAttribute('data-submenu'), 178 itemName = ev.target.innerHTML, 179 subMenuEl = self.el.querySelector('ul[data-menu="' + submenu + '"]'); 180 181 182 // check if there's a sub menu for this item 183 if( submenu && subMenuEl ) { 184 ev.preventDefault(); 185 // open it 186 self._openSubMenu(subMenuEl, pos, itemName); 187 } 188 else { 189 return false; 190 // add class current 191 var currentlink = self.el.querySelector('.menu__link--current'); 192 if( currentlink ) { 193 classie.remove(self.el.querySelector('.menu__link--current'), 'menu__link--current'); 194 } 195 classie.add(ev.target, 'menu__link--current'); 196 197 // callback 198 self.options.onItemClick(ev, itemName); 199 } 200 }); 201 }); 202 } 203 204 // back navigation 205 if( this.options.backCtrl ) { 206 207 this.backCtrl.addEventListener('click', function() { 208 self._back(); 209 }); 210 } 211 212 $('.menu__breadcrumbs').click(function(){ 213 if( $('.menu__breadcrumbs').hasClass('noBack') ) 214 window.location.href = $('.np_logo').attr('href'); 215 }); 216 }; 217 218 MLMenu.prototype._openSubMenu = function(subMenuEl, clickPosition, subMenuName) { 219 220 if( this.isAnimating ) { 221 return false; 222 } 223 this.isAnimating = true; 224 225 // save "parent" menu index for back navigation 226 this.menusArr[this.menus.indexOf(subMenuEl)].backIdx = this.current_menu; 227 // save "parent" menu´s name 228 this.menusArr[this.menus.indexOf(subMenuEl)].name = subMenuName; 229 // current menu slides out 230 this._menuOut(clickPosition); 231 // next menu (submenu) slides in 232 this._menuIn(subMenuEl, clickPosition); 233 }; 234 235 MLMenu.prototype._back = function() { 236 237 if( this.isAnimating ) { 238 return false; 239 } 240 this.isAnimating = true; 241 242 // current menu slides out 243 this._menuOut(); 244 // next menu (previous menu) slides in 245 var backMenu = this.menusArr[this.menusArr[this.current_menu].backIdx].menuEl; 246 this._menuIn(backMenu); 247 248 // remove last breadcrumb 249 if( this.options.breadcrumbsCtrl ) { 250 this.breadcrumbsCtrl.removeChild(this.breadcrumbsCtrl.lastElementChild); 251 } 252 }; 253 254 MLMenu.prototype._menuOut = function(clickPosition) { 255 256 // the current menu 257 var self = this, 258 currentMenu = this.menusArr[this.current_menu].menuEl, 259 isBackNavigation = typeof clickPosition == 'undefined' ? true : false; 260 261 // slide out current menu items - first, set the delays for the items 262 this.menusArr[this.current_menu].menuItems.forEach(function(item, pos) { 263 item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms'; 264 }); 265 // animation class 266 if( this.options.direction === 'r2l' ) { 267 classie.add(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); 268 } 269 else { 270 classie.add(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); 271 } 272 }; 273 274 MLMenu.prototype._menuIn = function(nextMenuEl, clickPosition) { 275 276 var self = this, 277 // the current menu 278 currentMenu = this.menusArr[this.current_menu].menuEl, 279 isBackNavigation = typeof clickPosition == 'undefined' ? true : false, 280 // index of the nextMenuEl 281 nextMenuIdx = this.menus.indexOf(nextMenuEl), 282 283 nextMenu = this.menusArr[nextMenuIdx], 284 nextMenuEl = nextMenu.menuEl, 285 nextMenuItems = nextMenu.menuItems, 286 nextMenuItemsTotal = nextMenuItems.length; 287 288 // slide in next menu items - first, set the delays for the items 289 nextMenuItems.forEach(function(item, pos) { 290 item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms'; 291 292 // we need to reset the classes once the last item animates in 293 // the "last item" is the farthest from the clicked item 294 // let's calculate the index of the farthest item 295 var farthestIdx = clickPosition <= nextMenuItemsTotal/2 || isBackNavigation ? nextMenuItemsTotal - 1 : 0; 296 297 if( pos === farthestIdx ) { 298 onEndAnimation(item, function() { 299 // reset classes 300 if( self.options.direction === 'r2l' ) { 301 classie.remove(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); 302 classie.remove(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); 303 } 304 else { 305 classie.remove(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); 306 classie.remove(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); 307 } 308 classie.remove(currentMenu, 'menu__level--current'); 309 classie.add(nextMenuEl, 'menu__level--current'); 310 311 //reset current 312 self.current_menu = nextMenuIdx; 313 self.options.backCtrl = 1; 314 // control back button and breadcrumbs navigation elements 315 if( !isBackNavigation ) { 316 // show back button 317 //self.options.backCtrl = 1; 318 if( self.options.backCtrl ) { 319 classie.remove(self.backCtrl, 'menu__back--hidden'); 320 $('nav.menu__breadcrumbs').removeClass('noBack').addClass('yesBack'); 321 } 322 323 // add breadcrumb 324 self._addBreadcrumb(nextMenuIdx); 325 } 326 else if( self.current_menu === 0 && self.options.backCtrl ) { 327 // hide back button 328 classie.add(self.backCtrl, 'menu__back--hidden'); 329 $('nav.menu__breadcrumbs').addClass('noBack').removeClass('yesBack'); 330 331 } 332 333 // we can navigate again.. 334 self.isAnimating = false;
335 336 // focus retention 337 nextMenuEl.focus(); 338 }); 339 } 340 }); 341 342 // animation class 343 if( this.options.direction === 'r2l' ) { 344 classie.add(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); 345 } 346 else { 347 classie.add(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); 348 } 349 }; 350 351 MLMenu.prototype._addBreadcrumb = function(idx) { 352 353 if( !this.options.breadcrumbsCtrl ) { 354 return false; 355 } 356 357 var bc = document.createElement('a'); 358 bc.href = '#'; // make it focusable 359 if(idx == 0) 360 bc.setAttribute('class','menu__link _home-icon'); // make it focusable 361 bc.innerHTML = idx ? this.menusArr[idx].name : this.options.initialBreadcrumb; 362 this.breadcrumbsCtrl.appendChild(bc); 363 364 var self = this; 365 bc.addEventListener('click', function(ev) { 366 ev.preventDefault(); 367 368 // do nothing if this breadcrumb is the last one in the list of breadcrumbs 369 if( !bc.nextSibling || self.isAnimating ) { 370 return false; 371 } 372 self.isAnimating = true; 373 // current menu slides out 374 self._menuOut(); 375 // next menu slides in 376 var nextMenu = self.menusArr[idx].menuEl; 377 self._menuIn(nextMenu); 378 379 // remove breadcrumbs that are ahead 380 var siblingNode; 381 while (siblingNode = bc.nextSibling) { 382 self.breadcrumbsCtrl.removeChild(siblingNode); 383 } 384 385 }); 386 }; 387 388 MLMenu.prototype._crawlCrumbs = function(currentMenu, menuArray) { 389 390 if (menuArray[currentMenu].backIdx != 0) { 391 this._crawlCrumbs(menuArray[currentMenu].backIdx, menuArray); 392 } 393 // create breadcrumb 394 this._addBreadcrumb(currentMenu); 395 } 396 397 window.MLMenu = MLMenu; 398 399})(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.