1// Global Javascript 2 3var rcpsgUI = (function (jQuery) { 4 5 //---------- Main Nav Handler 6 function mainNav() { 7 //--------- Desktop nav handler 8 if(jQuery(window).width() > 992) { 9 jQuery(document).on('mouseenter focusin', '.mnav-dropdown', function () { 10 jQuery(this).closest('li').addClass('mnav-dd-opened'); 11 }).on('mouseleave focusout', '.mnav-dropdown', function () { 12 jQuery('.mnav-dd-opened').removeClass('mnav-dd-opened'); 13 }); 14 15 16 } 17 18 //--------- Mobile nav handler 19 jQuery(document).on('click','.mm-trigger',function (){ 20 jQuery('html').toggleClass('mm-opened'); 21 }) 22 jQuery(document).on('click', '.mobile-dd-trigger', function () { 23 24 let parent = jQuery(this).parent('li'); 25 if(parent.hasClass('mnav-dd-opened')){ 26 parent.removeClass('mnav-dd-opened') 27 parent.find('.mnav-dropdown').slideUp() 28 } else { 29 parent.addClass('mnav-dd-opened') 30 parent.find('.mnav-dropdown').slideDown() 31 } 32 33 parent.siblings().removeClass('mnav-dd-opened') 34 parent.siblings().find('.mnav-dropdown').slideUp() 35 }) 36 } 37 38 //---------- Site Search Handler 39 function siteSearch() { 40 jQuery(document).on('click','.site-search--trigger', function (e){ 41 42 43 jQuery(this).toggleClass('search-active') 44 jQuery('.ss-cnt').toggleClass('ss-input-active'); 45 if (document.cookie.indexOf("rcpsgbanner=") < 0) { 46 jQuery('.ss-cnt').toggleClass('banner-active'); 47 } 48 49 jQuery(this).attr('aria-expanded', function (i, attr) { 50 return attr == 'false' ? 'true' : 'false'; 51 }) 52 53 if(jQuery(window).width() > 992){ 54 jQuery(this).hasClass('search-active')? 55 jQuery('.ss-search .form-search').focus() : jQuery(this).focus() 56 57 jQuery(document).on('focusout','.ss-search .form-submit', function (){ 58 jQuery('.site-search--trigger').focus() 59 jQuery('.site-search--trigger').removeClass('search-active').attr('aria-expanded','false') 60 jQuery('.ss-cnt').removeClass('ss-input-active') 61 }) 62 } 63 }) 64 jQuery("#edit-keys").attr('placeholder','Search...').attr('autocomplete','off') 65 } 66 67 //---------- Back to top button 68 function backToTopBtnHandler() { 69 jQuery(document).on('click','.btt', function (e) { 70 jQuery("html, body").animate({ scrollTop: 0 }, "slow"); 71 72 }) 73 74 // Handle Enter key press 75 jQuery(document).on('keydown', '.btt', function (e) { 76 if (e.key === 'Enter' || e.keyCode === 13) { 77 jQuery("html, body").animate({ scrollTop: 0 }, "slow"); 78 jQuery('.main-logo').focus(); 79 } 80 }); 81 } 82 83 84 //---------- Video Thumbnail 85 function videoThumbnailHandler() { 86 jQuery('.video-comprise').each(function() { 87 let videoIframe = jQuery(this).find('.v-video iframe'); 88 let videoSrc = videoIframe.data('src') || videoIframe.attr('src'); 89 let videoIdMatch = videoSrc.match(/(?:youtube\.com.*(?:\?v=|\/embed\/)|youtu\.be\/)(.{11})/); 90 if (videoIdMatch) { 91 let videoId = videoIdMatch[1]; 92 let videoImage = `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`; 93 94 // Attach the thumbnail image 95 jQuery(this).find('.v-video').append( 96 `<img class="v-thumbnail" src="${videoImage}" loading="lazy" alt="Video thumbnail">` 97 ); 98 } 99 videoIframe.attr('allow','autoplay') 100 }); 101 } 102 103 //---------- Video Play 104 function videoPlayHandler() { 105 jQuery(document).on('click', '.v-video-play-btn', function (e) { 106 e.preventDefault(); 107 let videoContainer = jQuery(this).closest('.v-video'); 108 videoContainer.addClass('video-played'); 109 110 var src = videoContainer.find('iframe').attr('src'); 111 videoContainer.find('iframe').attr('src', src + '?rel=0&autoplay=1') 112 }) 113 } 114 115 //--------- Tables handler 116 function tablesHandler() { 117 //***** Table wrapper 118 jQuery('.text-content table').wrap('<div class="table-cnt"></div>'); 119 jQuery('.text-content table :is(th,td)').attr( 'tabindex', '1') 120 jQuery('.text-content table tr th').attr('scope','col'); 121 jQuery('.table-cnt').each(function () { 122 let $table = jQuery(this).find('table'); 123 // Compare the widths 124 $table.outerWidth() > jQuery(this).outerWidth() && jQuery(this).addClass('scrollable-table'); 125 }); 126 127 128 129 jQuery(".text-content table").each(function() { 130 var table = jQuery(this); 131 var headers = []; 132 133 // Get the column headings from the first row of the thead 134 table.find("thead tr:first th").each(function() { 135 headers.push(jQuery(this).text().trim()); // Store header text 136 }); 137 138 // Assign data-heading attributes to each cell in the tbody 139 table.find("tbody tr").each(function() { 140 jQuery(this).find("td").each(function(index) { 141 if (headers[index]) { 142 jQuery(this).attr("data-heading", headers[index]); 143 } 144 }); 145 }); 146 }); 147 } 148 149 150 //--------- Accordions handler 151 function accordionsHandler() { 152 jQuery(document).on('click', '.acc-item-trigger',function (){ 153 console.log(jQuery(this)) 154 jQuery(this).siblings('.acc-item-content').slideToggle() 155 jQuery(this).closest('.acc-item').toggleClass('acc-selected'); 156 jQuery(this).attr('aria-expanded', function (i, attr) { 157 return attr == 'false' ? 'true' : 'false'; 158 }) 159 }) 160 } 161 162 //--------- External links handler 163 function addAriaToExternalLinks() { 164 const currentHost = location.hostname; 165 166 const externalCtaLinks = jQuery('.cta-btn[href^="http"]').filter(function () { 167 return new URL(this.href).hostname !== currentHost; 168 }); 169 170 const externalParagraphLinks = jQuery('p > a[href^="http"]').filter(function () { 171 return new URL(this.href).hostname !== currentHost; 172 }); 173
174 // Exit early if no external links found 175 if (externalCtaLinks.length === 0 && externalParagraphLinks.length === 0) { 176 return; 177 } 178 179 externalCtaLinks.each(function () { 180 jQuery(this) 181 .attr('aria-label', 'Opens external link in a new tab') 182 .attr('target', '_blank'); 183 }); 184 185 externalParagraphLinks.each(function () { 186 jQuery(this).attr('target', '_blank'); 187 }); 188 } 189 190 191 192 193 //--------- Zoom level detection 194 // function detectZoomHandler() { 195 // function isMobileDevice() { 196 // return /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent); 197 // } 198 // 199 // function detectZoom() { 200 // if (isMobileDevice()) { 201 // jQuery('html').removeClass('zoomed-in'); 202 // return; 203 // } 204 // 205 // const dpr = window.devicePixelRatio || 1; 206 // const zoomPercent = Math.round(dpr * 100); 207 // 208 // if (zoomPercent >= 200) { 209 // jQuery('html').addClass('zoomed-in'); 210 // jQuery('.main-nav-side').insertBefore('.main-nav'); 211 // 212 // jQuery('.main-nav > nav > ul > li:last-child > a').off('blur').on('blur', function () { 213 // jQuery('.mm-trigger').focus(); 214 // }); 215 // } else { 216 // jQuery('html').removeClass('zoomed-in'); 217 // jQuery('.main-nav').insertBefore('.main-nav-side'); 218 // } 219 // } 220 // 221 // jQuery(document).ready(detectZoom); 222 // jQuery(window).on('resize', detectZoom); 223 // } 224 function loadOomiUserSection() { 225 226 fetch('/udcustom/cart-count') 227 .then(response => { 228 return response.json(); 229 }) 230 .then(data => { 231 232 const container = document.getElementById('oomi-user-section'); 233 if (!container) { 234 console.warn('Could not find #oomi-user-section in the DOM'); 235 return; 236 } 237 238 if (data.cart_count != null) { 239 container.innerHTML = ` 240 <a class="cart" href="https://community.rcpsg.ac.uk/cart/index"> 241 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="25" viewBox="0 0 24 25" fill="none"> 242 <path d="M9.75 11.5518C9.94891 11.5518 10.1397 11.6308 10.2803 11.7714C10.421 11.9121 10.5 12.1028 10.5 12.3018V16.3018C10.5 16.5007 10.421 16.6914 10.2803 16.8321C10.1397 16.9727 9.94891 17.0518 9.75 17.0518C9.55109 17.0518 9.36032 16.9727 9.21967 16.8321C9.07902 16.6914 9 16.5007 9 16.3018V12.3018C9 12.1028 9.07902 11.9121 9.21967 11.7714C9.36032 11.6308 9.55109 11.5518 9.75 11.5518ZM15 12.3018C15 12.1028 14.921 11.9121 14.7803 11.7714C14.6397 11.6308 14.4489 11.5518 14.25 11.5518C14.0511 11.5518 13.8603 11.6308 13.7197 11.7714C13.579 11.9121 13.5 12.1028 13.5 12.3018V16.3018C13.5 16.5007 13.579 16.6914 13.7197 16.8321C13.8603 16.9727 14.0511 17.0518 14.25 17.0518C14.4489 17.0518 14.6397 16.9727 14.7803 16.8321C14.921 16.6914 15 16.5007 15 16.3018V12.3018Z" fill="#395B73"/> 243 <path d="M10.68 2.18818C10.8428 2.30229 10.9536 2.47634 10.9881 2.6721C11.0226 2.86785 10.9779 3.0693 10.864 3.23218L8.365 6.80218H15.635L13.135 3.23218C13.021 3.06907 12.9764 2.86734 13.0111 2.67136C13.0458 2.47539 13.1569 2.30122 13.32 2.18718C13.4831 2.07313 13.6848 2.02856 13.8808 2.06325C14.0768 2.09795 14.251 2.20907 14.365 2.37218L17.465 6.80218H19.75C20.3467 6.80218 20.919 7.03923 21.341 7.46119C21.7629 7.88314 22 8.45544 22 9.05218V9.83718C22 10.1722 21.9253 10.503 21.7812 10.8054C21.637 11.1078 21.4272 11.3742 21.167 11.5852L20.004 12.5282L19.201 18.8362C19.1319 19.3793 18.8671 19.8787 18.4562 20.2406C18.0453 20.6025 17.5165 20.8022 16.969 20.8022H7.031C6.48345 20.8022 5.95469 20.6025 5.54381 20.2406C5.13292 19.8787 4.86811 19.3793 4.799 18.8362L3.995 12.5282L2.833 11.5852C2.57278 11.3742 2.36296 11.1078 2.21884 10.8054C2.07473 10.503 1.99996 10.1722 2 9.83718V9.05218C2 8.45544 2.23705 7.88314 2.65901 7.46119C3.08097 7.03923 3.65326 6.80218 4.25 6.80218H6.534L9.636 2.37218C9.75011 2.20942 9.92417 2.09862 10.1199 2.06412C10.3157 2.02962 10.5171 2.07424 10.68 2.18818ZM19.75 8.30218H4.25C4.05109 8.30218 3.86032 8.3812 3.71967 8.52185C3.57902 8.6625 3.5 8.85327 3.5 9.05218V9.83718C3.5 10.0632 3.602 10.2772 3.778 10.4202L5.174 11.5522C5.32376 11.6739 5.42082 11.8487 5.445 12.0402L6.287 18.6472C6.31011 18.8282 6.39842 18.9945 6.53537 19.1151C6.67232 19.2357 6.84853 19.3022 7.031 19.3022H16.969C17.1515 19.3022 17.3277 19.2357 17.4646 19.1151C17.6016 18.9945 17.6899 18.8282 17.713 18.6472L18.554 12.0402C18.5784 11.8486 18.6759 11.6737 18.826 11.5522L20.222 10.4202C20.3088 10.3498 20.3789 10.261 20.427 10.1601C20.4751 10.0593 20.5 9.94893 20.5 9.83718V9.05218C20.5 8.85327 20.421 8.6625 20.2803 8.52185C20.1397 8.3812 19.9489 8.30218 19.75 8.30218Z" fill="#395B73"/> 244 </svg>
245 <span>${data.cart_count}</span> 246 </a> 247 <a href="https://community.rcpsg.ac.uk/Account/Dashboard" target="_blank" aria-label="Dashboard" class="dashboard-btn"> 248 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 249 <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/> 250 </svg> 251 </a> 252 <a href="https://community.rcpsg.ac.uk/account/logout" target="_blank" class="user-btn">Log out</a> 253 `; 254 } else { 255 container.innerHTML = ` 256 <a href="https://sso.rcpsg.ac.uk/" class="user-btn">Log in</a> 257 <a href="/membership" target="_blank" class="user-btn user-btn-gray" data-position="top right" data-intent="join">Join us</a> 258 `; 259 } 260 }) 261 .catch(error => { 262 console.error('Error in fetch /udcustom/cart-count:', error); 263 }); 264 } 265 266 function siteBanner() { 267 if (document.cookie.indexOf("rcpsgbanner=") < 0) { 268 jQuery('.site-banner').css('display','block'); 269 jQuery('body').addClass('banner-visible'); 270 } 271 jQuery('.site-banner__link').click(function(e){ 272 e.preventDefault(); 273 var expiryDate = new Date(); 274 expiryDate.setMonth(expiryDate.getMonth() + 1); 275 document.cookie = 'rcpsgbanner=1; expires=' + expiryDate.toGMTString() + ';' ; 276 jQuery('.site-banner').hide(); 277 jQuery('body').removeClass('banner-visible'); 278 }); 279 } 280 281 //--------- Multiple Download Files Handler 282 function MultiDownloadHandler() { 283 jQuery('.para-multiple-file-downloads').each(function () { 284 const $container = jQuery(this); 285 const $downloads = $container.find('.download-wrapper'); 286 287 if ($downloads.length > 4) { 288 289 $downloads.slice(4).hide(); // Hide all after the 4th 290 291 const $btn = $container.find('.show-download-files'); 292 293 $btn.on('click', function () { 294 $container.toggleClass('download-files-shown'); 295 296 const isShown = $container.hasClass('download-files-shown'); 297 298 $downloads.slice(4).toggle(isShown); 299 300 $btn.find('.show-download-files--text').text(isShown ? 'Show less' : 'Show all'); 301 }); 302 } else { 303 // Hide button if 4 or fewer items 304 $container.find('.show-download-files').hide(); 305 } 306 }); 307 } 308 309 310 //--------- Image Gallery component 311 function galleryComponentHandler() { 312 // Check if the gallery component exists 313 if (!jQuery('.para-gallery').length) { 314 return; // Exit early if gallery component is not on the page 315 } 316 317 // Modal handler 318 const modal = document.getElementById('imageModal'); 319 if (!modal) return; 320 321 const modalImg = modal.querySelector('#modalImage'); 322 const modalTitle = modal.querySelector('#modalTitle'); 323 const modalDesc = modal.querySelector('#modalDesc'); 324 const closeBtn = modal.querySelector('.modal-close'); 325 const overlay = modal.querySelector('.modal-overlay'); 326 327 if (!modalImg || !modalTitle || !modalDesc || !closeBtn || !overlay) return; 328 329 // Optional: Add a loading spinner inside the modal 330 const spinner = document.createElement('div'); 331 spinner.className = 'modal-spinner'; 332 spinner.innerText = 'No image to show'; 333 modal.querySelector('.modal-content').appendChild(spinner); 334 spinner.style.display = 'none'; 335 336 jQuery('.gallery-card-nolink').each(function () { 337 jQuery(this).on('click', function () { 338 const img = this.querySelector('img'); 339 const title = this.querySelector('h3')?.innerText || ''; 340 const desc = this.querySelector('p')?.innerText || ''; 341 342 modalImg.src = ''; 343 modalImg.alt = ''; 344 modalTitle.innerText = ''; 345 modalDesc.innerText = ''; 346 347 modal.removeAttribute('hidden'); 348 spinner.style.display = 'block'; 349 350 const newImg = new Image(); 351 newImg.onload = function () { 352 modalImg.src = newImg.src; 353 modalImg.alt = img.alt; 354 modalTitle.innerText = title;
355 modalDesc.innerText = desc; 356 spinner.style.display = 'none'; 357 }; 358 newImg.src = img.src; 359 360 closeBtn.focus(); 361 }); 362 }); 363 364 function closeModal() { 365 modal.setAttribute('hidden', true); 366 modalImg.src = ''; 367 } 368 369 closeBtn.addEventListener('click', closeModal); 370 overlay.addEventListener('click', closeModal); 371 document.addEventListener('keydown', function (e) { 372 if (e.key === 'Escape') { 373 closeModal(); 374 } 375 }); 376 377 // View more button handler 378 jQuery('.para-gallery').each(function () { 379 const galleryCard = jQuery(this).find('.gallery-card'); 380 const _thisButton = jQuery(this).find('.toggle-gallery-btn'); 381 382 if (galleryCard.length > 4) { 383 _thisButton.show(); 384 } 385 }); 386 387 jQuery(document).on('click', '.toggle-gallery-btn', function () { 388 const btn = jQuery(this); 389 const closestCardsCnt = btn.siblings('.gallery-cards'); 390 btn.text(btn.text() === 'Show more' ? 'Show less' : 'Show more'); 391 btn.closest('.para-gallery').find('.gc-hidden').toggleClass('gc-shown'); 392 if(jQuery(window).width() > 992) { 393 jQuery('html, body').animate({ scrollTop: closestCardsCnt.offset().top - 100 }, 'slow'); 394 } 395 }); 396 } 397 398 399 return { 400 mainNav:mainNav, 401 siteSearch:siteSearch, 402 backToTopBtnHandler:backToTopBtnHandler, 403 videoThumbnailHandler:videoThumbnailHandler, 404 videoPlayHandler:videoPlayHandler, 405 tablesHandler:tablesHandler, 406 accordionsHandler:accordionsHandler, 407 addAriaToExternalLinks:addAriaToExternalLinks, 408 // detectZoomHandler:detectZoomHandler, 409 loadOomiUserSection:loadOomiUserSection, 410 siteBanner:siteBanner, 411 MultiDownloadHandler:MultiDownloadHandler, 412 galleryComponentHandler:galleryComponentHandler 413 } 414}(jQuery)); 415 416 417jQuery(document).ready(function () { 418 rcpsgUI.mainNav(); 419 rcpsgUI.siteSearch(); 420 rcpsgUI.backToTopBtnHandler(); 421 rcpsgUI.videoThumbnailHandler(); 422 rcpsgUI.videoPlayHandler(); 423 rcpsgUI.tablesHandler(); 424 rcpsgUI.accordionsHandler(); 425 rcpsgUI.addAriaToExternalLinks(); 426 // rcpsgUI.detectZoomHandler(); 427 rcpsgUI.loadOomiUserSection(); 428 rcpsgUI.siteBanner(); 429 rcpsgUI.MultiDownloadHandler(); 430 rcpsgUI.galleryComponentHandler(); 431}) 432 433
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.