1(function () { 2 'use strict'; 3 4 define(function () { 5 6 /** 7 * @param {string} selector 8 * @param {Object} options 9 * @constructor 10 */ 11 var easyAccessNav = function (selector, options) { 12 /** {Object} */ 13 var settings = {}; 14 /** {Object} */ 15 var storage = { 16 isOpen: false, 17 preventClick: false 18 }; 19 20 // Settings 21 options = (options) ? options : {}; 22 settings.openClass = (options.openClass) ? options.openClass : 'open'; 23 settings.hoverDelay = (options.hoverDelay) ? options.hoverDelay : 300; 24 settings.openOnHover = (options.openOnHover === false) ? options.openOnHover : true; 25 settings.useCategoryAsPage = (options.useCategoryAsPage) ? options.useCategoryAsPage : false; 26 settings.closeOnPageClick = (options.closeOnPageClick === false) ? options.closeOnPageClick : true; 27 28 if (options.verticalMenu) { 29 settings.openOnHover = false; 30 settings.useCategoryAsPage = false; 31 settings.closeOnPageClick = false; 32 } 33 34 // Elements 35 storage.navElement = document.querySelector(selector); 36 37 if (!storage.navElement) { 38 return; 39 } 40 41 // Events 42 document.addEventListener('touchstart', registerEventType); 43 document.addEventListener('touchend', handleEvent); 44 document.addEventListener('mouseup', handleEvent); 45 document.addEventListener('keyup', handleEvent); 46 document.addEventListener('mouseover', handleEvent); 47 document.addEventListener('click', handleClickEvent); 48 49 /** 50 * @param {Event} event 51 */ 52 function registerEventType (event) { 53 // Register touch - multiple events will be fired after it, which we need to prevent 54 if (event.type === 'touchstart') { 55 storage.ignoreNonTouchEvents = true; 56 } 57 } 58 59 /** 60 * @param {Event} event 61 */ 62 function handleEvent (event) { 63 // Reset touch event check - 'mouseup' is the last event fired with touch 64 if (storage.ignoreNonTouchEvents && event.type === 'mouseup') { 65 storage.ignoreNonTouchEvents = false; 66 return; 67 } 68 69 // Prevent multiple events firing in case of touch 70 if (storage.ignoreNonTouchEvents && event.type !== 'touchend') { 71 return; 72 } 73 74 var element = (event.type === 'keyup') ? document.activeElement : event.target; 75 76 switch (event.type) { 77 case 'touchend': 78 handleTouchEnd(element); 79 break; 80 case 'mouseup': 81 handleMouseUp(element); 82 break; 83 case 'keyup': 84 handleKeyUp(element); 85 break; 86 case 'mouseover': 87 handleMouseOver(element); 88 break; 89 } 90 } 91 92 /** 93 * @param {Event} event 94 */ 95 function handleClickEvent (event) { 96 var element = event.target; 97 if (!isAnchorInNav(element)) { 98 return; 99 } 100 101 if (storage.preventClick) { 102 event.preventDefault(); 103 storage.preventClick = false; 104 } 105 } 106 107 /** 108 * @param {Element} element 109 */ 110 function handleTouchEnd (element) { 111 var isLinkInNav = isAnchorInNav(element); 112 113 if (settings.closeOnPageClick && !isLinkInNav) { 114 closeSubmenu(event); 115 return; 116 } 117 118 if (!isLinkInNav) { 119 return; 120 } 121 122 if (!hasSubmenu(element)) { 123 return; 124 } 125 126 if (!settings.useCategoryAsPage) { 127 storage.preventClick = true; 128 } 129 130 if (!hasOpenClass(element)) { 131 storage.preventClick = true; 132 openSubmenu(element); 133 return; 134 } 135 136 if (!settings.useCategoryAsPage) { 137 closeSubmenu(); 138 } 139 } 140 141 /** 142 * @param {Element} element 143 */ 144 function handleMouseUp (element) { 145 var isLinkInNav = isAnchorInNav(element); 146 147 if (settings.closeOnPageClick && !isLinkInNav) { 148 closeSubmenu(event); 149 return; 150 } 151 152 if (!isLinkInNav) { 153 return; 154 } 155 156 if (!hasSubmenu(element)) { 157 return; 158 } 159 160 if (!settings.useCategoryAsPage) { 161 storage.preventClick = true; 162 } 163 164 if (settings.openOnHover) { 165 return; 166 } 167 168 if (!hasOpenClass(element)) { 169 openSubmenu(element); 170 return; 171 } 172 173 if (!settings.useCategoryAsPage) { 174 closeSubmenu(); 175 } 176 } 177 178 /** 179 * @param {Element} element 180 */ 181 function handleKeyUp (element) { 182 if (!isAnchorInNav(element)) { 183 closeSubmenu(); 184 return; 185 } 186 187 openSubmenu(element); 188 } 189 190 /** 191 * @param {Element} element 192 */ 193 function handleMouseOver (element) { 194 if (!settings.openOnHover) { 195 return; 196 } 197 198 if (!isElementInNav(element)) { 199 closeSubmenuWithDelay(); 200 return; 201 } 202 203 if (!hasSubmenu(element) && !isSubmenuElement(element)) { 204 closeSubmenuWithDelay(); 205 return; 206 } 207 208 clearHoverTimeout(); 209 210 if (!hasOpenClass(element)) { 211 openSubmenu(element); 212 return; 213 } 214 215 if (!settings.useCategoryAsPage) { 216 closeSubmenu(); 217 } 218 } 219 220 /** 221 * @param {Element} element 222 */ 223 function openSubmenu (element) { 224 removeOpenClasses(); 225 addOpenClassToParents(element); 226 } 227 228 /** 229 * Close submenu 230 */ 231 function closeSubmenu () {
232 if (!storage.isOpen) { 233 return; 234 } 235 236 removeOpenClasses(); 237 } 238 239 /** 240 * Close submenu with a delay 241 */ 242 function closeSubmenuWithDelay () { 243 if (!storage.isOpen) { 244 return; 245 } 246 247 clearHoverTimeout(); 248 storage.hoverTimeoutID = window.setTimeout(function () { 249 removeOpenClasses(); 250 }, settings.hoverDelay); 251 } 252 253 /** 254 * Clear hover timeout 255 */ 256 function clearHoverTimeout () { 257 if (typeof storage.hoverTimeoutID === 'number') { 258 window.clearTimeout(storage.hoverTimeoutID); 259 delete storage.hoverTimeoutID; 260 } 261 } 262 263 /** 264 * @param {Object} element 265 */ 266 function addOpenClassToParents (element) { 267 element = getCorrectParentIgnoringNormalLinks(element); 268 269 while (element && element !== storage.navElement) { 270 if (element.tagName.toUpperCase() === 'LI') { 271 element.classList.add(settings.openClass); 272 storage.isOpen = true; 273 } 274 275 element = element.parentElement; 276 } 277 } 278 279 /** 280 * Remove all open classes 281 */ 282 function removeOpenClasses () { 283 var openElements = storage.navElement.querySelectorAll('.' + settings.openClass); 284 var i; 285 286 for (i = 0; i < openElements.length; i++) { 287 openElements[i].classList.remove(settings.openClass); 288 } 289 290 storage.isOpen = false; 291 } 292 293 /** 294 * @param element 295 * @returns {Element} 296 */ 297 function getCorrectParentIgnoringNormalLinks (element) { 298 if (element.tagName.toUpperCase() === 'A' && !element.classList.contains('sub')) { 299 return element.parentElement.parentElement; 300 } 301 302 return element.parentElement; 303 } 304 305 /** 306 * @param {Element} element 307 * @returns {boolean} 308 */ 309 function isAnchorInNav (element) { 310 if (element.tagName.toUpperCase() !== 'A') { 311 return false; 312 } 313 314 return storage.navElement.contains(element); 315 } 316 317 /** 318 * @param {Element} element 319 * @returns {boolean} 320 */ 321 function isElementInNav (element) { 322 return storage.navElement.contains(element); 323 } 324 325 /** 326 * @param {Element} element 327 * @returns {boolean} 328 */ 329 function isSubmenuElement (element) { 330 var i = 0; 331 332 while (element && element !== storage.navElement) { 333 if (element.tagName.toUpperCase() === 'UL') { 334 i++; 335 } 336 337 if (i === 2) { 338 return true; 339 } 340 341 element = element.parentElement; 342 } 343 344 return false; 345 } 346 347 /** 348 * @param element 349 * @returns {boolean} 350 */ 351 function hasSubmenu (element) { 352 return element.classList.contains('sub'); 353 } 354 355 /** 356 * @param element 357 * * @returns {boolean} 358 */ 359 function hasOpenClass (element) { 360 return element.parentElement && element.parentElement.classList.contains(settings.openClass); 361 } 362 }; 363 364 return easyAccessNav; 365 366 }); 367})();
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.