PageSourceSearch

https://www.cardiffstudents.com/stylesheet/october2012/subnav.js?v=6

js cardiffstudents.com collected 2026-10-02 07:34:45 UTC 13,739 bytes, 359 lines download raw bytes

1//############################################################################//
2//                                                                            //
3//                                  SUBNAV                                    //
4//                                                                            //
5//############################################################################//
6
7/**
8 * Subnav Constructor.
9 * 
10 * This function will populate a Subnav with a series of HTML code blocks
11 * to display headings and context menus.
12 * 
13 * @param   object   tiles   associative array of tpopiles to be displayed.
14 */
15function populateSubnav(subpages) {
16    $.each(subpages, function(key, value) {
17        var height= document.getElementById("subnavList").offsetTop+54;
18        var heightMobile = document.getElementById("subnavListMobile").offsetTop+54;
19        var idName = key.replace(/\s/g, '').replace(/'/g, '');
20        
21        var list = "";
22        var listMobile = "";
23        if (typeof value.children !== "undefined") {
24            list = `<div id="${idName}List" class="contextnav top-[${height}px] z-30 absolute left-0 w-full gap-2 bg-union-teal drop-shadow-xl text-black py-3 justify-center items-center flex-nowrap hidden"> </div>`
25            listMobile = `<div id="${idName}ListMobile" class=" contextnavMobile left-0 absolute w-full gap-2 rounded-b mt-3 bg-union-teal drop-shadow-lg text-black py-2 z-10 justify-center items-center flex-nowrap hidden">`
26        }
27        
28        $("#subnavList").append(`
29            <div id="${idName}HeadingContainer" class="subnavHeading z-30 mx-auto text-neutral-100 px-0 rounded-t-lg mt-2 pb-3 pt-1 text-center">
30                <a href="${value.link}" id="${idName}Heading" class="z-30 grow pt-2 font-union-bold pb-2 whitespace-nowrap px-6 text-2xl items-center w-32 lg:w-full">${key}</a>
31                ${list}
32            </div>
33            `);
34            
35        $("#subnavListMobile").append(`
36            <div id="${idName}HeadingContainerMobile" class="subnavHeadingMobile mx-auto text-neutral-100 px-3 rounded-full my-3 text-center snap-center snap-mandatory">
37                <a id="${idName}Heading" ${typeof value.children === "undefined" ? `href="${value.link}"` : ""} class="grow text-lg pt-2 font-union-bold pb-2 whitespace-nowrap items-center w-32">${key}</a>
38                ${listMobile}
39                </div>
40            </div>
41            `);
42        
43        $("#"+idName+"ListMobile").append( `<a id="${idName}ContainerMobile" href="${value.link}" class="flex text-lg my-2 items-center font-union-bold justify-center text-white hover:no-underline hover:bg-neutral-900 shrink rounded-full">${key}</a>`);
44        
45        if (typeof value.children !== "undefined") {
46            $.each(value.children, function(subsection, subsectionLink){
47            var desktopText = 
48                    `<span class="flex"><a id="${subsection.replace(/\s/g, '')}Container" href="${subsectionLink}" class="z-30 flex text-2xl items-center font-union-bold justify-center hover:text-white/90 hover:no-underline px-4 w-full hover:bg-neutral-800 rounded-full">${subsection}
49                    </a></span>`;
50                $("#"+idName+"List").append(desktopText);
51                
52            var mobileText = 
53                    `<a id="${subsection.replace(/\s/g, '')}ContainerMobile" href="${subsectionLink}" class="flex text-lg my-2 items-center font-union-bold justify-center text-white/90 hover:no-underline hover:bg-neutral-800 shrink rounded-full">${subsection}
54                    </a>`;
55                $("#"+idName+"ListMobile").append(mobileText);
56            })
57        }
58    })
59}
60
61/**
62 * Single Subnav Constructor.
63 * 
64 * This function will populate a Subnav without a series of dropdowns. For example,
65 * this is present on the Events Page
66 * 
67 * @param   object   tiles   associative array of tiles to be displayed.
68 */
69function populateSingleSubnav(subpages) { 
70    $.each(subpages, function(key, value){
71        var subnavList = document.getElementById("subnavList");
72        var subnavListMobile = document.getElementById("subnavListMobile");
73        var contextnavList = document.getElementById("subnav");
74        var contextnavListMobile = document.getElementById("subnavMobile");
75        var height= subnavList.offsetTop+56;
76        var heightMobile = subnavList.offsetTop+56;
77        var desktopHeadings = 
78            (`
79                <div id="${key.replace(/\s/g, '')}HeadingContainer" class="subnavHeading mx-auto text-neutral-100 px-3 rounded-full my-3 text-center">
80                    <a href="${value.link}" id="${key.replace(/\s/g, '')}Heading" class="grow pt-2 font-union-bold pb-2 whitespace-nowrap px-6 text-2xl items-center w-32 lg:w-full">${key}</a>
81                    <div id="${key.replace(/\s/g, '')}List" class="top-[${height}px] z-30 absolute left-0 w-full gap-2 bg-union-teal drop-shadow-xl text-black py-3 justify-center items-center flex-nowrap hidden">
82        
83                    </div>
84                </div>
85                    
86            `);
87        var mobileHeadings = 
88            (`
89                <div id="${key.replace(/\s/g, '')}HeadingContainerMobile" class="subnavHeadingMobile mx-auto text-neutral-100 px-3 rounded-full my-3 text-center snap-center snap-mandatory">
90                    <a href="${value.link}" id="${key.replace(/\s/g, '')}Heading" class="grow text-lg pt-2 font-union-bold pb-2 whitespace-nowrap items-center w-32">${key}</a>
91                    <div id="${key.replace(/\s/g, '')}ListMobile" class="left-0 absolute w-full gap-2 rounded-b mt-3 bg-union-teal drop-shadow-lg text-black py-2 z-10 justify-center items-center flex-nowrap hidden">
92        
93                    </div>
94                </div>
95                    
96            `);
97
98        subnavList.innerHTML += desktopHeadings;
99        subnavListMobile.innerHTML += mobileHeadings;
100        
101        var target = document.getElementById(key.replace(/\s/g, '')+"List");
102        var targetMobile = document.getElementById(key.replace(/\s/g, '')+"ListMobile");
103        var mobileExtraText = 
104            `<a id="${key.replace(/\s/g, '')}ContainerMobile" href="${value.link}" class="flex text-lg my-2 items-center font-union-bold justify-center text-white hover:no-underline hover:bg-neutral-900 shrink rounded-full">${key}</a>`;
105        targetMobile.innerHTML += mobileExtraText;
106    })
107}
108
109/**
110 * Set Active Page.
111 * 
112 * This function will set a page as "Active" within the subnav menu.
113 * 
114 * @param   object   tiles   associative array of tiles to be displayed.
115 */
116function setActivePage(subpages) {
117    var pagePath = window.location.pathname.split("/").filter(Boolean); // Remove empty strings
118
119    $.each(subpages, function(key, value) {
120        let menuPath = value.link.split("/").filter(Boolean); // Remove empty strings
121
122        // Check if the menu item has children
123        if (value.children && Object.keys(value.children).length > 0) {
124            // Original logic for parent items with children
125            if (pagePath.includes(menuPath[menuPath.length - 1])) {
126                $('#subnavHeaderText').text(key);
127                setActive(key.replace(/\s/g, ''));
128                setActiveSubpage(value);
129            }
130        } else {
131            // New logic: only highlight the deepest segment for single links
132            let lastSegment = pagePath[pagePath.length - 1];
133            if (menuPath[menuPath.length - 1] === lastSegment) {
134                $('#subnavHeaderText').text(key);
135                setActive(key.replace(/\s/g, ''));
136                setActiveSubpage(value);
137            }
138        }
139    });
140}
141
142
143
144/**
145 * Set Active Subpage.
146 * 
147 * This function will set a subpage as "Active" within the subnav menu.
148 * (To remember, when adding subpages, will need to prefix a slash to the end
149 * of the URL as we check 2 layers back)
150 * 
151 * @param   object   tiles   associative array of tiles to be displayed.
152 */
153function setActiveSubpage(subpages) {
154    var pagePath = window.location.pathname.split("/");
155    if (typeof subpages['children'] !== "undefined") {
156        for (const [key, value] of Object.entries(subpages['children'])) {
157            currentPagePath = value.split("/");
158            if (pagePath.includes(currentPagePath[currentPagePath.length-2])) {
159                $('#subnavHeaderText').text(key);
160                
161                var heading = document.getElementById(key.replace(/\s/g, '')+"Container");
162                addActiveCSS(heading);
163    
164                heading = document.getElementById(key.replace(/\s/g, '')+"ContainerMobile");
165                addActiveCSS(heading);
166                heading.classList.add("mx-5");
167            }
168        }
169    }
170}
171
172function addActiveCSS(heading) {
173    heading.classList.remove("hover:bg-neutral-800", "hover:text-white/90");
174    heading.classList.add("bg-neutral-900", "text-white");
175}
176
177/**
178 * Set Active.
179 *
180 * get the current page on the navbar. Add active class to current page to the
181 * navbar and also the heading container.
182 *
183 * @param   string  page    page name.
184 *
185 * @return
186 */
187function setActive(page) {
188    page = page.replace(/'/g, '');
189    var nav = document.getElementById(page+"List");
190    if (nav !== null) {
191        nav.classList.add("lg:flex");
192        nav.classList.add("subnavListActive");
193        nav.classList.add("subnavListActive");
194    }
195    
196    var heading = document.getElementById(page+"HeadingContainer");
197    heading.classList.remove("subnavHeading");
198    heading.classList.add("bg-union-teal");
199    heading.classList.add("subnavActive");
200    
201    heading = document.getElementById(page+"HeadingContainerMobile");
202    heading.classList.remove("subnavHeading");
203    heading.classList.toggle("subnavActive");
204}
205
206function removeActiveMobileHeading() {
207    $('#subnavListMobile').children().each(function () {
208        if ($(this).is(':hover')) {
209        }
210    });
211}
212
213/**
214 * Add Hyperlinks to Subnav.
215 *
216 * Loops through all subnav tags and validates if a hyperlink can be
217 * added. If valid and sanitised; update the href with the text value of its
218 * tag.
219 *
220 * @return
221 */
222function addLinksToSubnav() {
223    $('#subnavList').children().each(function () {
224
225        let validated = validateSubnavTag($(this));
226        
227        if (!validated) {
228            return;
229        }
230        
231        let subnavTitle = sanitiseSubnavString($(this).children('a').text());
232
233        $(this).children('a').attr('href', window.location.pathname + subnavTitle);
234    });
235}
236
237/**
238 * Add Hyperlinks to Sub subnav.
239 *
240 * Loops through all sub subnav tags and validates if a hyperlink can be
241 * added. If valid and sanitised; update the href with the text value of it
242 * tag.
243 *
244 * @return
245 */
246function addLinksToSubSubnav() {
247    $('#subnavList').find('span').each(function () {
248
249        let validated = validateSubnavTag($(this));
250        
251        if (!validated) {
252            return;
253        }
254
255        let subnavChildTitle = sanitiseSubnavString($(this).children('a').text());
256
257        let correctPath = getCorrectPathname(window.location.pathname);
258
259        $(this).children('a').attr('href', window.location.pathname + correctPath + subnavChildTitle);
260    });
261}
262
263/**
264 * Validate Subnav Tag.
265 *
266 * Return if tag is undefined, if text of tag is empty or whitespaces, and
267 * if the hyperlink of tag is empty or returns to home.
268 *
269 * @param   Object  tag Object to be validated.
270 *
271 * @return  boolean
272 */
273function validateSubnavTag(tag) {
274    if (typeof $(tag) === 'undefined') {
275        return false;
276    }
277
278    if (isEmptyOrSpaces($(tag).text())) {
279        return false;
280    }
281
282    if ($(tag).children('a').attr('href') !== '/' && $(tag).children('a').attr('href') !== '') {
283        return false;
284    }
285    
286    return true;
287}
288
289/**
290 * Sanitise subnav string.
291 *
292 * Trim the string of whitespace, replace all inner whitespace with a
293 * hypthen, and convert to lower case.
294 *
295 * @param   string  subnavText  Inner text of subnav.
296 *
297 * @return  string
298 */
299function sanitiseSubnavString(subnavText) {
300    return subnavText.trim().replace(/ /g, '-').toLowerCase();
301}
302
303/**
304 * Get Correct Pathname.
305 *
306 * Split the pathname by forward slashses. If we are one level deep than the
307 * parent path then go up one level. Otherwise don't go up a level.
308 *
309 * @param   string  pathname    URL folder string.
310 *
311 * @return  string
312 */
313function getCorrectPathname(pathname) {
314    pathname = pathname.split('/');
315    
316    let previousPathLevel = '../';
317    let upLevel = '';
318    let prevPathLength = recursiveUpPath(pathname.length);
319    
320    pathLength = pathname.length - prevPathLength;
321
322    for (let i = 0; i < pathLength; i++) {
323        upLevel = upLevel + previousPathLevel;
324    }
325
326    return upLevel;
327}
328
329/**
330 * Recursive Up Path.
331 *
332 * Recursively go up one level of the pathname. Minus one from the path
333 * length, and check if it 
334 */
335function recursiveUpPath(pathnameLength) {
336    // pathnameLength = pathnameLength - 1;
337
338    if (pathnameLength > 5) {
339        return recursiveUpPath(pathnameLength - 1);
340    }
341
342    return pathnameLength;
343}
344
345/**
346 * Check if string is empty or has whitespace.
347 *
348 * Check if string is empty or if the string is just whitespace.
349 *
350 * @param   string  str Subnav text.
351 *
352 * @return  boolean
353 */
354function isEmptyOrSpaces(str) {
355    return str === null || str.match(/^ *$/) !== null;
356}
357
358addLinksToSubnav();
359addLinksToSubSubnav();

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.