PageSourceSearch

https://www.psbbau.at/js/_essentials/ruffnav.js

js psbbau.at collected 2026-10-10 02:14:00 UTC 11,676 bytes, 277 lines download raw bytes

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

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.