1/** 2 * off-canvas nav 3 * @name ruffnav 4 * @author Dominik Kressler 5 * @version 2.2.6 6 * @thanks Jasmin Reiff 7 * @edit by Malte Zoudlik 8 * @edit by Dario Rieke 9 * @copyright (c) 2018 10 */ 11 function Ruffnav(options) { 12 this.options = J.merge({ 13 toggler: '#navi-toggled', 14 container: '#navigation', 15 navList: '.navi-main', 16 subParent: '.has-sub', 17 subClass: '.sub', 18 backText: false, 19 mobileBreak: 'large', 20 closeOnScroll: true, 21 desktopHover: false, 22 navSplit: false, 23 navAppend: false 24 }, options); 25 this.depth = 0; 26 this.toggler = document.querySelector(this.options.toggler); 27 this.container = document.querySelector(this.options.container); 28 this.navList = this.container.querySelector(this.options.navList) 29 this.sub = this.container.querySelectorAll(this.options.subClass); 30 this.subParent = this.container.querySelectorAll(this.options.subParent); 31 this.html = document.documentElement; 32 this.device = breakpoint(this.options.mobileBreak) ? 'desktop' : 'mobile'; 33 this.isOpen = false; 34 this.subToggler = null; 35 this.windowSize = window.innerWidth; 36 this.navSize = 0; 37 this.activePath = ''; 38 this.path = window.location.pathname.split('/').filter(function(el) {return el != ''}); 39 40 // set back button text by language, or by overwrite 41 if(this.options.backText === false) 42 { 43 var html = document.querySelector('html'); 44 this.options.backText = (html.lang == 'de') ? 'Zurück' : 'back'; 45 } 46 47 this.backButton = create('li', { 48 'class': 'nav-back', 49 'tabindex': '0' 50 }, '<span>'+this.options.backText+'</span>'); 51 52 var self = this; 53 this.init = function() { 54 if(!self.navList) return console.warn(`No element found with class "${self.options.navList}". Ruffnav could't be initialized. Please add the class to your main navigation UL-Element`); 55 // add class for desktop hover 56 self.container.classList.add('first-level'); 57 if(self.options.desktopHover) self.container.classList.add('hover-navigation'); 58 59 // append custom Content to Navigation 60 if(self.options.navAppend) { 61 var itemToAppend = create('li', {'class': 'nav-item appended-item'}, self.options.navAppend); 62 self.navList.appendChild(itemToAppend); 63 } 64 65 self.navSize = self.navList.childNodes.length; 66 // split navigation in half 67 68 // transform Navigation markup 69 [].forEach.call(self.subParent, function(parentItem) { 70 var childNode = parentItem.firstElementChild, 71 categoryLabel = (childNode.dataset && childNode.dataset.categoryLabel) ? childNode.dataset.categoryLabel : childNode.innerHTML; 72 73 // parent-name attribute for UL's 74 childNode.nextElementSibling.setAttribute('data-parent-name', childNode.innerText); 75 76 // only clone when Element is a link 77 if(childNode.nodeName !== 'SPAN') { 78 // var cActive = (window.location.origin+window.location.pathname === childNode.href) ? 'active' : ''; 79 80 // copy link inside UL's 81 childNode.nextElementSibling.insertBefore(create('li', { 82 'class': 'nav-item '+childNode.className.trim() 83 }, '<a href="'+childNode.href+'" title="'+childNode.title+'">'+categoryLabel+'</a>'), parentItem.children[1].children[0]); 84 if(self.path.length) { 85 self.activeClassFallback(childNode); 86 } 87 } 88 // replace link's with span's 89 parentItem.replaceChild(create('span', { 90 'class': 'sub-toggler '+childNode.className.trim(), 91 'tabindex': 0, 92 'data-href': childNode.href 93 }, childNode.innerHTML), childNode) 94 }); 95 [].forEach.call(self.sub, function(subItem){ 96 // add backbuttons to UL's 97 subItem.insertBefore(self.backButton.cloneNode(true), subItem.childNodes[0]); 98 }); 99 100 if(self.options.navSplit) { 101 var elm; 102 var splitList = create('ul', {class: "navi split-list"}); 103 for (var i = parseInt(self.navSize / 2); i != splitList.childNodes.length; i) { 104 elm = self.navList.childNodes[i].cloneNode(true);
105 splitList.appendChild(elm) 106 self.navList.childNodes[i].remove(); 107 } 108 self.navList.parentElement.appendChild(splitList); 109 } 110 111 // close on mobile toggle 112 self.toggler.addEventListener('click', self._closeAll); 113 114 // add Listeners for different events 115 self.addListener(); 116 117 // add global resize listener 118 self.listenResize(); 119 120 return self; 121 } 122 this._open = function(e) { 123 // open event 124 var target, sameLevelCurrents; 125 126 if(e.type === 'mouseenter' && self.device === "mobile") return; 127 if(e.type === 'mouseenter') { 128 target = e.target; 129 target.childNodes[1].classList.add('current'); 130 if(self.container.querySelector('.open') != null) self.container.querySelector('.open').classList.remove('open'); 131 target.childNodes[1].classList.add('open'); 132 } else { 133 target = e.currentTarget; 134 135 //close other on same level when already opened 136 sameLevelCurrents = target.parentElement.parentElement.querySelector('.current'); 137 if(sameLevelCurrents !== null) self.closeRecusive(sameLevelCurrents, false) 138 139 target.nextElementSibling.classList.add('current'); 140 if(self.container.querySelector('.open') != null) self.container.querySelector('.open').classList.remove('open'); 141 target.nextElementSibling.classList.add('open'); 142 } 143 if(!self.isOpen && self.device !== 'mobile') { 144 if(self.options.closeOnScroll) window.addEventListener('scroll', self._closeAll); 145 document.addEventListener('click', self._closeAll); 146 document.addEventListener('keydown', self._closeAll); 147 self.isOpen = true; 148 } 149 self.container.classList.remove('first-level'); 150 self.scrollTop(); 151 } 152 this._close = function(e) { 153 // close event 154 var currentListTree; 155 156 if(e.type === 'mouseleave' && self.device === "mobile") return; 157 if(e.type === 'mouseleave') currentListTree = e.currentTarget; 158 else currentListTree = e.currentTarget.parentElement; 159 160 self.closeRecusive(currentListTree, false); 161 if(self.container.querySelectorAll('ul.current').length === 0) self.isOpen = false; 162 if(self.container.querySelectorAll('ul.current').length === 0) self.container.classList.add('first-level'); 163 164 self.scrollTop(); 165 } 166 this._closeAll = function(e) { 167 (self.toggler.checked) ? self.html.classList.add('hidden-scroll') : self.html.classList.remove('hidden-scroll'); 168 // close all 169 if(e.type === 'scroll') { 170 self.closeRecusive(null, true); 171 } else if(e.type === 'click') { 172 // check if click was not inside navigation 173 if(e.target === self.container || self.container.contains(e.target)) return; 174 else self.closeRecusive(null, true) 175 } else if(e.type === 'keydown') { 176 // check if "enter" was pressed 177 if(e.keyCode === 27) self.closeRecusive(null, true); 178 } else { 179 self.closeRecusive(null, true); 180 } 181 self.removeListeners(); 182 self.isOpen = false 183 } 184 this.closeRecusive = function(firstList, closeAll) { 185 // close all open items 186 if(closeAll) { 187 [].forEach.call(self.container.querySelectorAll('.current'), function(openSubs) {openSubs.classList.remove('current')}); 188 if(self.container.querySelector('.open') != null) self.container.querySelector('.open').classList.remove('open'); 189 self.container.classList.add('first-level'); 190 } else { 191 if(self.container.querySelector('.open') != null) self.container.querySelector('.open').classList.remove('open'); 192 [].forEach.call(firstList.querySelectorAll('.current'), function(openSubs){ 193 openSubs.classList.remove('current'); 194 }); 195 firstList.parentNode.parentNode.classList.add('open'); 196 firstList.classList.remove('current'); 197 } 198 } 199 this.removeListeners = function() { 200 // remove eventListener for global close 201 window.removeEventListener('scroll', self._closeAll); 202 document.removeEventListener('keydown', self._closeAll); 203 document.removeEventListener('click', self._closeAll); 204 } 205 this.addListener = function() { 206 // add eventListener to subToggle and back 207 self.subToggler = self.container.querySelectorAll('.sub-toggler'); 208 self.backButtons = self.container.querySelectorAll('.nav-back'); 209 [].forEach.call(self.subToggler, function(subTogglerItem){ 210 if(self.options.desktopHover) { 211 subTogglerItem.parentNode.addEventListener('mouseenter', self._open); 212 subTogglerItem.parentNode.addEventListener('mouseleave', self._close); 213 } 214 subTogglerItem.addEventListener('click', self._open); 215 }); 216 [].forEach.call(self.backButtons, function(backButtonsItem){ 217 backButtonsItem.addEventListener('click', self._close, false); 218 }); 219 } 220 this.listenResize = function() { 221 // global resize Event 222 window.onresize = function(){ 223 self.device = breakpoint(self.options.mobileBreak) ? 'desktop' : 'mobile'; 224 if(self.device === 'mobile') self.removeListeners(); 225 if(self.windowSize != window.innerWidth && self.device === 'desktop') { 226 self.closeRecusive(null, true); 227 self.isOpen = false;
228 } 229 self.windowSize = window.innerWidth; 230 } 231 } 232 233 this.scrollTop = function() { 234 [].forEach.call(self.sub, function(item) {item.scrollTop = 0} ) 235 self.container.childNodes[0].scrollTop = 0; 236 } 237 238 function create(type, attr, content) { 239 // create Node element 240 var elm = document.createElement(type); 241 [].forEach.call(Object.entries(attr), function(name){ 242 elm.setAttribute(name[0], name[1]); 243 }); 244 if(content) elm.innerHTML = content; 245 return elm 246 } 247 248 this.activeClassFallback = function(elem) { 249 if(elem.href === window.location.origin + '/' + this.activePath + this.path[0]+'/') { 250 elem.classList.add('active'); 251 if(elem.parentNode.classList.contains(self.options.subParent.substring(1))) elem.parentNode.classList.add('active') 252 if(this.path.length === 1) elem.parentNode.querySelector('[href="'+window.location.origin+'/' + this.activePath + this.path[0]+'/"]').classList.add('active'); 253 this.activePath += this.path[0] + '/'; 254 this.path.splice(0,1); 255 } 256 } 257 258 this.init(); 259} 260if (!Object.entries) 261Object.entries = function( obj ){ 262 var ownProps = Object.keys( obj ), 263 i = ownProps.length, 264 resArray = new Array(i); // preallocate the Array 265 while (i--) 266 resArray[i] = [ownProps[i], obj[ownProps[i]]]; 267 268 return resArray; 269}; 270if (!('remove' in Element.prototype)) { 271 Element.prototype.remove = function() { 272 if (this.parentNode) this.parentNode.removeChild(this); 273 }; 274}
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.