/*! * classie v1.0.1 * class helper functions * from bonzo https://github.com/ded/bonzo * MIT license * * classie.has( elem, 'my-class' ) -> true/false * classie.add( elem, 'my-new-class' ) * classie.remove( elem, 'my-unwanted-class' ) * classie.toggle( elem, 'my-class' ) */ /*jshint browser: true, strict: true, undef: true, unused: true */ /*global define: false, module: false */ ( function( window ) { 'use strict'; // class helper functions from bonzo https://github.com/ded/bonzo function classReg( className ) { return new RegExp("(^|\\s+)" + className + "(\\s+|$)"); } // classList support for class management // altho to be fair, the api sucks because it won't accept multiple classes at once var hasClass, addClass, removeClass; if ( 'classList' in document.documentElement ) { hasClass = function( elem, c ) { return elem.classList.contains( c ); }; addClass = function( elem, c ) { elem.classList.add( c ); }; removeClass = function( elem, c ) { elem.classList.remove( c ); }; } else { hasClass = function( elem, c ) { return classReg( c ).test( elem.className ); }; addClass = function( elem, c ) { if ( !hasClass( elem, c ) ) { elem.className = elem.className + ' ' + c; } }; removeClass = function( elem, c ) { elem.className = elem.className.replace( classReg( c ), ' ' ); }; } function toggleClass( elem, c ) { var fn = hasClass( elem, c ) ? removeClass : addClass; fn( elem, c ); } var classie = { // full names hasClass: hasClass, addClass: addClass, removeClass: removeClass, toggleClass: toggleClass, // short names has: hasClass, add: addClass, remove: removeClass, toggle: toggleClass }; // transport if ( typeof define === 'function' && define.amd ) { // AMD define( classie ); } else if ( typeof exports === 'object' ) { // CommonJS module.exports = classie; } else { // browser global window.classie = classie; } })( window ); /*! modernizr 3.2.0 (Custom Build) | MIT * * http://modernizr.com/download/?-cssanimations-prefixed !*/ !function(e,n,t){function r(e,n){return typeof e===n}function o(){var e,n,t,o,i,s,a;for(var f in C)if(C.hasOwnProperty(f)){if(e=[],n=C[f],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;td;d++)if(v=e[d],h=z.style[v],a(v,"-")&&(v=s(v)),z.style[v]!==t){if(i||r(o,"undefined"))return l(),"pfx"==n?v:!0;try{z.style[v]=o}catch(g){}if(z.style[v]!=h)return l(),"pfx"==n?v:!0}return l(),!1}function h(e,n,t,o,i){var s=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+b.join(s+" ")+s).split(" ");return r(n,"string")||r(n,"undefined")?v(a,n,o,i):(a=(e+" "+N.join(s+" ")+s).split(" "),u(a,n,t))}function y(e,n,r){return h(e,t,t,n,r)}var g=[],C=[],_={_version:"3.2.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,n){var t=this;setTimeout(function(){n(t[e])},0)},addTest:function(e,n,t){C.push({name:e,fn:n,options:t})},addAsyncTest:function(e){C.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=_,Modernizr=new Modernizr;var w=n.documentElement,x="svg"===w.nodeName.toLowerCase(),S="Moz O ms Webkit",b=_._config.usePrefixes?S.split(" "):[];_._cssomPrefixes=b;var E=function(n){var r,o=prefixes.length,i=e.CSSRule;if("undefined"==typeof i)return t;if(!n)return!1;if(n=n.replace(/^@/,""),r=n.replace(/-/g,"_").toUpperCase()+"_RULE",r in i)return"@"+n;for(var s=0;o>s;s++){var a=prefixes[s],f=a.toUpperCase()+"_"+r;if(f in i)return"@-"+a.toLowerCase()+"-"+n}return!1};_.atRule=E;var N=_._config.usePrefixes?S.toLowerCase().split(" "):[];_._domPrefixes=N;var P={elem:f("modernizr")};Modernizr._q.push(function(){delete P.elem});var z={style:P.elem.style};Modernizr._q.unshift(function(){delete z.style}),_.testAllProps=h;_.prefixed=function(e,n,t){return 0===e.indexOf("@")?E(e):(-1!=e.indexOf("-")&&(e=s(e)),n?h(e,n,t):h(e,"pfx"))};_.testAllProps=y,Modernizr.addTest("cssanimations",y("animationName","a",!0)),o(),i(g),delete _.addTest,delete _.addAsyncTest;for(var T=0;T´s) this.menus = [].slice.call(this.el.querySelectorAll('.menu__level')); // index of current menu // Each level is actually a different menu so 0 is root, 1 is sub-1, 2 sub-2, etc. this.current_menu = 0; /* Determine what current menu actually is */ var current_menu; this.menus.forEach(function(menuEl, pos) { var items = menuEl.querySelectorAll('.menu__item'); items.forEach(function(itemEl, iPos) { var currentLink = itemEl.querySelector('.menu__link--current'); if (currentLink) { // This is the actual menu__level that should have current current_menu = pos; } }); }); if (current_menu) { this.current_menu = current_menu; } this._init(); } MLMenu.prototype.options = { // show breadcrumbs breadcrumbsCtrl : true, // initial breadcrumb text initialBreadcrumb : 'all', // show back button backCtrl : true, // delay between each menu item sliding animation itemsDelayInterval : 60, // direction direction : 'r2l', // callback: item that doesn´t have a submenu gets clicked // onItemClick([event], [inner HTML of the clicked item]) onItemClick : function(ev, itemName) { return false; } }; MLMenu.prototype._init = function() { // iterate the existing menus and create an array of menus, // more specifically an array of objects where each one holds the info of each menu element and its menu items this.menusArr = []; this.breadCrumbs = false; var self = this; var submenus = []; /* Loops over root level menu items */ this.menus.forEach(function(menuEl, pos) { var menu = {menuEl : menuEl, menuItems : [].slice.call(menuEl.querySelectorAll('.menu__item'))}; self.menusArr.push(menu); // set current menu class if( pos === self.current_menu ) { classie.add(menuEl, 'menu__level--current'); } var menu_x = menuEl.getAttribute('data-menu'); var links = menuEl.querySelectorAll('.menu__link'); links.forEach(function(linkEl, lPos) { var submenu = linkEl.getAttribute('data-submenu'); if (submenu) { var pushMe = {"menu":submenu, "name": linkEl.innerHTML }; if (submenus[pos]) { submenus[pos].push(pushMe); } else { submenus[pos] = [] submenus[pos].push(pushMe); } } }); }); /* For each MENU, find their parent MENU */ this.menus.forEach(function(menuEl, pos) { var menu_x = menuEl.getAttribute('data-menu'); submenus.forEach(function(subMenuEl, menu_root) { subMenuEl.forEach(function(subMenuItem, subPos) { if (subMenuItem.menu == menu_x) { self.menusArr[pos].backIdx = menu_root; self.menusArr[pos].name = subMenuItem.name; } }); }); }); // create breadcrumbs if( self.options.breadcrumbsCtrl ) { this.breadcrumbsCtrl = document.createElement('nav'); this.breadcrumbsCtrl.className = 'menu__breadcrumbs'; this.breadcrumbsCtrl.setAttribute('aria-label', 'You are here'); this.el.insertBefore(this.breadcrumbsCtrl, this.el.firstChild); // add initial breadcrumb this._addBreadcrumb(0); // Need to add breadcrumbs for all parents of current submenu if (self.menusArr[self.current_menu].backIdx != 0 && self.current_menu != 0) { this._crawlCrumbs(self.menusArr[self.current_menu].backIdx, self.menusArr); this.breadCrumbs = true; } // Create current submenu breadcrumb if (self.current_menu != 0) { this._addBreadcrumb(self.current_menu); this.breadCrumbs = true; } } // create back button if (this.options.backCtrl) { this.backCtrl = document.createElement('button'); if (this.breadCrumbs) { this.backCtrl.className = 'menu__back'; } else { this.backCtrl.className = 'menu__back menu__back--hidden'; } this.backCtrl.setAttribute('aria-label', 'Go back'); this.backCtrl.innerHTML = ''; this.el.insertBefore(this.backCtrl, this.el.firstChild); } // event binding this._initEvents(); }; MLMenu.prototype._initEvents = function() { var self = this; for(var i = 0, len = this.menusArr.length; i < len; ++i) { this.menusArr[i].menuItems.forEach(function(item, pos) { item.querySelector('a').addEventListener('click', function(ev) { var submenu = ev.target.getAttribute('data-submenu'), itemName = ev.target.innerHTML, subMenuEl = self.el.querySelector('ul[data-menu="' + submenu + '"]'); // check if there's a sub menu for this item if( submenu && subMenuEl ) { ev.preventDefault(); // open it self._openSubMenu(subMenuEl, pos, itemName); } else { // add class current var currentlink = self.el.querySelector('.menu__link--current'); if( currentlink ) { classie.remove(self.el.querySelector('.menu__link--current'), 'menu__link--current'); } classie.add(ev.target, 'menu__link--current'); // callback self.options.onItemClick(ev, itemName); } }); }); } // back navigation if( this.options.backCtrl ) { this.backCtrl.addEventListener('click', function() { self._back(); }); } }; MLMenu.prototype._openSubMenu = function(subMenuEl, clickPosition, subMenuName) { if( this.isAnimating ) { return false; } this.isAnimating = true; // save "parent" menu index for back navigation this.menusArr[this.menus.indexOf(subMenuEl)].backIdx = this.current_menu; // save "parent" menu´s name this.menusArr[this.menus.indexOf(subMenuEl)].name = subMenuName; // current menu slides out this._menuOut(clickPosition); // next menu (submenu) slides in this._menuIn(subMenuEl, clickPosition); }; MLMenu.prototype._back = function() { if( this.isAnimating ) { return false; } this.isAnimating = true; // current menu slides out this._menuOut(); // next menu (previous menu) slides in var backMenu = this.menusArr[this.menusArr[this.current_menu].backIdx].menuEl; this._menuIn(backMenu); // remove last breadcrumb if( this.options.breadcrumbsCtrl ) { this.breadcrumbsCtrl.removeChild(this.breadcrumbsCtrl.lastElementChild); } }; MLMenu.prototype._menuOut = function(clickPosition) { // the current menu var self = this, currentMenu = this.menusArr[this.current_menu].menuEl, isBackNavigation = typeof clickPosition == 'undefined' ? true : false; // slide out current menu items - first, set the delays for the items this.menusArr[this.current_menu].menuItems.forEach(function(item, pos) { item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms'; }); // animation class if( this.options.direction === 'r2l' ) { classie.add(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); } else { classie.add(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); } }; MLMenu.prototype._menuIn = function(nextMenuEl, clickPosition) { var self = this, // the current menu currentMenu = this.menusArr[this.current_menu].menuEl, isBackNavigation = typeof clickPosition == 'undefined' ? true : false, // index of the nextMenuEl nextMenuIdx = this.menus.indexOf(nextMenuEl), nextMenu = this.menusArr[nextMenuIdx], nextMenuEl = nextMenu.menuEl, nextMenuItems = nextMenu.menuItems, nextMenuItemsTotal = nextMenuItems.length; // slide in next menu items - first, set the delays for the items nextMenuItems.forEach(function(item, pos) { item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms'; // we need to reset the classes once the last item animates in // the "last item" is the farthest from the clicked item // let's calculate the index of the farthest item var farthestIdx = clickPosition <= nextMenuItemsTotal/2 || isBackNavigation ? nextMenuItemsTotal - 1 : 0; if( pos === farthestIdx ) { onEndAnimation(item, function() { // reset classes if( self.options.direction === 'r2l' ) { classie.remove(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); classie.remove(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); } else { classie.remove(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); classie.remove(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); } classie.remove(currentMenu, 'menu__level--current'); classie.add(nextMenuEl, 'menu__level--current'); //reset current self.current_menu = nextMenuIdx; // control back button and breadcrumbs navigation elements if( !isBackNavigation ) { // show back button if( self.options.backCtrl ) { classie.remove(self.backCtrl, 'menu__back--hidden'); } // add breadcrumb self._addBreadcrumb(nextMenuIdx); } else if( self.current_menu === 0 && self.options.backCtrl ) { // hide back button classie.add(self.backCtrl, 'menu__back--hidden'); } // we can navigate again.. self.isAnimating = false; // focus retention nextMenuEl.focus(); }); } }); // animation class if( this.options.direction === 'r2l' ) { classie.add(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); } else { classie.add(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); } }; MLMenu.prototype._addBreadcrumb = function(idx) { if( !this.options.breadcrumbsCtrl ) { return false; } var bc = document.createElement('a'); bc.href = '#'; // make it focusable bc.innerHTML = idx ? this.menusArr[idx].name : this.options.initialBreadcrumb; this.breadcrumbsCtrl.appendChild(bc); var self = this; bc.addEventListener('click', function(ev) { ev.preventDefault(); // do nothing if this breadcrumb is the last one in the list of breadcrumbs if( !bc.nextSibling || self.isAnimating ) { return false; } self.isAnimating = true; // current menu slides out self._menuOut(); // next menu slides in var nextMenu = self.menusArr[idx].menuEl; self._menuIn(nextMenu); // remove breadcrumbs that are ahead var siblingNode; while (siblingNode = bc.nextSibling) { self.breadcrumbsCtrl.removeChild(siblingNode); } }); }; MLMenu.prototype._crawlCrumbs = function(currentMenu, menuArray) { if (menuArray[currentMenu].backIdx != 0) { this._crawlCrumbs(menuArray[currentMenu].backIdx, menuArray); } // create breadcrumb this._addBreadcrumb(currentMenu); } window.MLMenu = MLMenu; })(window); (function () { var menuEl = document.getElementById('ml-menu'), mlmenu = new MLMenu(menuEl, { // breadcrumbsCtrl : true, // show breadcrumbs // initialBreadcrumb : 'all', // initial breadcrumb text backCtrl: true, // show back button // itemsDelayInterval : 60, // delay between each menu item sliding animation // onItemClick: loadDummyData // callback: item that doesn´t have a submenu gets clicked - onItemClick([event], [inner HTML of the clicked item]) }); // mobile menu toggle var openMenuCtrl = document.querySelector('.action--open'), closeMenuCtrl = document.querySelector('.action--close'); openMenuCtrl.addEventListener('click', openMenu); closeMenuCtrl.addEventListener('click', closeMenu); function openMenu() { classie.add(menuEl, 'menu--open'); closeMenuCtrl.focus(); } function closeMenu() { classie.remove(menuEl, 'menu--open'); openMenuCtrl.focus(); } })();