1(function (window) { 2 3 'use strict'; 4 5 var support = { 6 animations: Modernizr.cssanimations 7 }, 8 animEndEventNames = { 9 'WebkitAnimation': 'webkitAnimationEnd', 10 'OAnimation': 'oAnimationEnd', 11 'msAnimation': 'MSAnimationEnd', 12 'animation': 'animationend' 13 }, 14 animEndEventName = animEndEventNames[Modernizr.prefixed('animation')], 15 onEndAnimation = function (el, callback) { 16 var onEndCallbackFn = function (ev) { 17 if (support.animations) { 18 if (ev.target != this) return; 19 this.removeEventListener(animEndEventName, onEndCallbackFn); 20 } 21 if (callback && typeof callback === 'function') { 22 callback.call(); 23 } 24 }; 25 if (support.animations) { 26 el.addEventListener(animEndEventName, onEndCallbackFn); 27 } else { 28 onEndCallbackFn(); 29 } 30 }; 31 32 function extend(a, b) { 33 for (var key in b) { 34 if (b.hasOwnProperty(key)) { 35 a[key] = b[key]; 36 } 37 } 38 return a; 39 } 40 41 function MLMenu(el, options) { 42 this.el = el; 43 this.options = extend({}, this.options); 44 extend(this.options, options); 45 46 // the menus (<ul>´s) 47 this.menus = [].slice.call(this.el.querySelectorAll('.menu__level')); 48 // index of current menu 49 this.current = 0; 50 51 this._init(); 52 } 53 54 MLMenu.prototype.options = { 55 // show breadcrumbs 56 breadcrumbsCtrl: true, 57 // initial breadcrumb text 58 initialBreadcrumb: 'all', 59 // show back button 60 backCtrl: true, 61 // delay between each menu item sliding animation 62 itemsDelayInterval: 60, 63 // direction 64 direction: 'r2l', 65 // callback: item that doesn´t have a submenu gets clicked 66 // onItemClick([event], [inner HTML of the clicked item]) 67 onItemClick: function (ev, itemName) { 68 return false; 69 } 70 }; 71 72 MLMenu.prototype._init = function () { 73 // 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 74 this.menusArr = []; 75 var self = this; 76 this.menus.forEach(function (menuEl, pos) { 77 var menu = { 78 menuEl: menuEl, 79 menuItems: [].slice.call(menuEl.querySelectorAll('.menu__item')) 80 }; 81 self.menusArr.push(menu); 82 83 // set current menu class 84 if (pos === self.current) { 85 classie.add(menuEl, 'menu__level--current'); 86 } 87 }); 88 89 // create back button 90 if (this.options.backCtrl) { 91 this.backCtrl = document.createElement('button'); 92 this.backCtrl.className = 'menu__back menu__back--hidden'; 93 this.backCtrl.setAttribute('aria-label', 'Go back'); 94 this.backCtrl.innerHTML = '<i class="xi-arrow-left"></i>'; 95 this.el.insertBefore(this.backCtrl, this.el.firstChild); 96 } 97 98 99 // create breadcrumbs 100 if (self.options.breadcrumbsCtrl) { 101 this.breadcrumbsCtrl = document.createElement('nav'); 102 this.breadcrumbsCtrl.className = 'menu__breadcrumbs'; 103 this.el.insertBefore(this.breadcrumbsCtrl, this.el.firstChild); 104 // add initial breadcrumb 105 this._addBreadcrumb(0); 106 } 107 108 // event binding 109 this._initEvents(); 110 }; 111 112 MLMenu.prototype._initEvents = function () { 113 var self = this; 114 115 for (var i = 0, len = this.menusArr.length; i < len; ++i) { 116 this.menusArr[i].menuItems.forEach(function (item, pos) { 117 item.querySelector('a').addEventListener('click', function (ev) { 118 var submenu = ev.target.getAttribute('data-submenu'), 119 itemName = ev.target.innerHTML, 120 subMenuEl = self.el.querySelector('ul[data-menu="' + submenu + '"]'); 121 122 // check if there's a sub menu for this item 123 if (submenu && subMenuEl) { 124 ev.preventDefault(); 125 // open it 126 self._openSubMenu(subMenuEl, pos, itemName); 127 } else { 128 // add class current 129 var currentlink = self.el.querySelector('.menu__link--current'); 130 if (currentlink) { 131 classie.remove(self.el.querySelector('.menu__link--current'), 'menu__link--current'); 132 } 133 classie.add(ev.target, 'menu__link--current'); 134 135 // callback 136 self.options.onItemClick(ev, itemName); 137 } 138 }); 139 }); 140 } 141 142 // back navigation 143 if (this.options.backCtrl) { 144 this.backCtrl.addEventListener('click', function () { 145 self._back(); 146 }); 147 } 148 }; 149 150 MLMenu.prototype._openSubMenu = function (subMenuEl, clickPosition, subMenuName) { 151 if (this.isAnimating) { 152 return false;
153 } 154 this.isAnimating = true; 155 156 // save "parent" menu index for back navigation 157 this.menusArr[this.menus.indexOf(subMenuEl)].backIdx = this.current; 158 // save "parent" menu´s name 159 this.menusArr[this.menus.indexOf(subMenuEl)].name = subMenuName; 160 // current menu slides out 161 this._menuOut(clickPosition); 162 // next menu (submenu) slides in 163 this._menuIn(subMenuEl, clickPosition); 164 }; 165 166 MLMenu.prototype._back = function () { 167 if (this.isAnimating) { 168 return false; 169 } 170 this.isAnimating = true; 171 172 // current menu slides out 173 this._menuOut(); 174 // next menu (previous menu) slides in 175 var backMenu = this.menusArr[this.menusArr[this.current].backIdx].menuEl; 176 this._menuIn(backMenu); 177 178 // remove last breadcrumb 179 if (this.options.breadcrumbsCtrl) { 180 this.breadcrumbsCtrl.removeChild(this.breadcrumbsCtrl.lastElementChild); 181 } 182 }; 183 184 MLMenu.prototype._menuOut = function (clickPosition) { 185 // the current menu 186 var self = this, 187 currentMenu = this.menusArr[this.current].menuEl, 188 isBackNavigation = typeof clickPosition == 'undefined' ? true : false; 189 190 // slide out current menu items - first, set the delays for the items 191 this.menusArr[this.current].menuItems.forEach(function (item, pos) { 192 item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms'; 193 }); 194 // animation class 195 if (this.options.direction === 'r2l') { 196 classie.add(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); 197 } else { 198 classie.add(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); 199 } 200 }; 201 202 MLMenu.prototype._menuIn = function (nextMenuEl, clickPosition) { 203 var self = this, 204 // the current menu 205 currentMenu = this.menusArr[this.current].menuEl, 206 isBackNavigation = typeof clickPosition == 'undefined' ? true : false, 207 // index of the nextMenuEl 208 nextMenuIdx = this.menus.indexOf(nextMenuEl), 209 210 nextMenuItems = this.menusArr[nextMenuIdx].menuItems, 211 nextMenuItemsTotal = nextMenuItems.length; 212 213 // slide in next menu items - first, set the delays for the items 214 nextMenuItems.forEach(function (item, pos) { 215 item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms'; 216 217 // we need to reset the classes once the last item animates in 218 // the "last item" is the farthest from the clicked item 219 // let's calculate the index of the farthest item 220 var farthestIdx = clickPosition <= nextMenuItemsTotal / 2 || isBackNavigation ? nextMenuItemsTotal - 1 : 0; 221 222 if (pos === farthestIdx) { 223 onEndAnimation(item, function () { 224 // reset classes 225 if (self.options.direction === 'r2l') { 226 classie.remove(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); 227 classie.remove(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); 228 } else { 229 classie.remove(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight'); 230 classie.remove(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); 231 } 232 classie.remove(currentMenu, 'menu__level--current'); 233 classie.add(nextMenuEl, 'menu__level--current'); 234 235 //reset current 236 self.current = nextMenuIdx; 237 238 // control back button and breadcrumbs navigation elements 239 if (!isBackNavigation) { 240 // show back button 241 if (self.options.backCtrl) { 242 classie.remove(self.backCtrl, 'menu__back--hidden'); 243 } 244 245 // add breadcrumb 246 self._addBreadcrumb(nextMenuIdx); 247 } else if (self.current === 0 && self.options.backCtrl) { 248 // hide back button 249 classie.add(self.backCtrl, 'menu__back--hidden'); 250 } 251 252 // we can navigate again.. 253 self.isAnimating = false; 254 }); 255 } 256 }); 257 258 // animation class 259 if (this.options.direction === 'r2l') { 260 classie.add(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); 261 } else { 262 classie.add(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft'); 263 } 264 }; 265 266 MLMenu.prototype._addBreadcrumb = function (idx) { 267 if (!this.options.breadcrumbsCtrl) { 268 return false;
269 } 270 271 var bc = document.createElement('a'); 272 bc.innerHTML = idx ? this.menusArr[idx].name : this.options.initialBreadcrumb; 273 this.breadcrumbsCtrl.appendChild(bc); 274 275 var self = this; 276 bc.addEventListener('click', function (ev) { 277 ev.preventDefault(); 278 279 // do nothing if this breadcrumb is the last one in the list of breadcrumbs 280 if (!bc.nextSibling || self.isAnimating) { 281 return false; 282 } 283 self.isAnimating = true; 284 285 // current menu slides out 286 self._menuOut(); 287 // next menu slides in 288 var nextMenu = self.menusArr[idx].menuEl; 289 self._menuIn(nextMenu); 290 291 // remove breadcrumbs that are ahead 292 var siblingNode; 293 while (siblingNode = bc.nextSibling) { 294 self.breadcrumbsCtrl.removeChild(siblingNode); 295 } 296 }); 297 }; 298 299 // ì´ê¸°í ì¶ê° 300 MLMenu.prototype.resetMenu = function () { 301 if (this.isAnimating || this.current === 0) { 302 return; 303 } 304 this.isAnimating = true; 305 306 // íì¬ ë©ë´ë¥¼ ì¨ê¸°ê¸° 307 this._menuOut(); 308 309 // 첫 ë²ì§¸ ë©ë´ë¡ ì´ë 310 var firstMenu = this.menusArr[0].menuEl; 311 this._menuIn(firstMenu); 312 313 // ë¸ë ëí¬ë¼ 리ì 314 if (this.options.breadcrumbsCtrl) { 315 while (this.breadcrumbsCtrl.firstChild) { 316 this.breadcrumbsCtrl.removeChild(this.breadcrumbsCtrl.firstChild); 317 } 318 this._addBreadcrumb(0); 319 } 320 321 // ë¤ë¡ ê°ê¸° ë²í¼ ì¨ê¸°ê¸° 322 if (this.options.backCtrl) { 323 classie.add(this.backCtrl, "menu__back--hidden"); 324 } 325 326 // íì¬ ì¸ë±ì¤ë¥¼ 0ì¼ë¡ ë³ê²½ 327 this.current = 0; 328 329 this.isAnimating = false; 330 }; 331 // 332 333 window.MLMenu = MLMenu; 334 335})(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.