1"use strict"; 2 3/** 4 * Responsive GPT ads. 5 * 6 * GPT renders each ad inside a fixed-size, cross-origin iframe. On narrow 7 * screens a creative wider than its container (e.g. a 320px leaderboard on a 8 * ~300px viewport) gets cut off, because the iframe's content cannot reflow. 9 * 10 * To keep the whole ad visible we scale the rendered iframe down with a CSS 11 * transform so its width fills the ad container and its height follows the 12 * creative's aspect ratio. The served size comes from GPT's `slotRenderEnded` 13 * event; we also rescale on resize and run a fallback pass for ads that may 14 * have rendered before the listener was attached. 15 * 16 * Every GPT placement holds its iframe in one of two containers: 17 * - `.ad-main` â the iframe is a direct child. Covers the top 18 * leaderboard (parts/ads/ad-top.php), the middle/bottom 19 * leaderboards (ad-leaderboard-2.php, ad-bottom.php), 20 * in-article ads (ad-in-article.php + single.php), 21 * the post-grid rectangle (PostGridView.php) and the 22 * sidebar rectangle (ad-sidebar*.php). 23 * - `.iframe-contain` â in-content "ad" bloks, e.g. the middle of the 24 * homepage (blok-party/Views/AdView.php). 25 * 26 * The blok wraps its `.iframe-contain` in an outer `.ad-main`, so that outer 27 * div matches `.ad-main` too â but its real GPT iframe lives in the nested 28 * `.iframe-contain` (matched and scaled on its own). We skip any `.ad-main` 29 * that contains a `.iframe-contain` to avoid sizing against the wrong box and 30 * tracking the same iframe twice. 31 */ 32(function () { 33 var ads = []; // { el, width, height } 34 35 // Containers whose direct child is the GPT iframe we scale. 36 var AD_SELECTOR = '.ad-main, .iframe-contain'; 37 function isAdContainer(el) { 38 if (!el || typeof el.className !== 'string') { 39 return false; 40 } 41 // Blok in-content ad: the GPT iframe sits directly in `.iframe-contain`. 42 if (el.className.indexOf('iframe-contain') !== -1) { 43 return true; 44 } 45 // Every other placement holds the iframe directly in `.ad-main`. Exclude 46 // the blok's outer `.ad-main` wrapper, which nests a `.iframe-contain`. 47 return el.className.indexOf('ad-main') !== -1 && !el.querySelector('.iframe-contain'); 48 } 49 function applyScale(entry) { 50 var el = entry.el; 51 var iframe = el.querySelector('iframe'); 52 if (!iframe || !entry.width || !entry.height) { 53 return; 54 } 55 var available = el.clientWidth; 56 if (!available) { 57 return; 58 } 59 60 // Only ever scale down â never blow a small creative up past 1:1. 61 var scale = Math.min(1, available / entry.width); 62 63 // Pin the iframe to the served creative size. GPT sets these as width/height 64 // attributes, but a global responsive-media reset (iframe { height: auto }) 65 // overrides the height attribute and collapses the frame to the browser's 66 // 150px iframe default, clipping the creative. An inline style wins over it. 67 // `.iframe-contain iframe` also caps width at 100% of the container; clear 68 // that so the pinned width holds and the transform below scales to fit. 69 iframe.style.maxWidth = 'none'; 70 iframe.style.width = entry.width + 'px'; 71 iframe.style.height = entry.height + 'px'; 72 iframe.style.transformOrigin = 'top center'; 73 iframe.style.transform = scale < 1 ? 'scale(' + scale + ')' : ''; 74 75 // Reserve the scaled height so the section below doesn't overlap the ad, 76 // and clip the now-empty space left by the transform. 77 el.style.height = Math.ceil(entry.height * scale) + 'px'; 78 el.style.overflow = 'hidden'; 79 } 80 function track(el, width, height) { 81 if (!el || !width || !height) { 82 return; 83 } 84 ads = ads.filter(function (e) { 85 return e.el !== el; 86 }); 87 var entry = { 88 el: el, 89 width: width, 90 height: height 91 }; 92 ads.push(entry); 93 applyScale(entry); 94 } 95 function rescaleAll() { 96 for (var i = 0; i < ads.length; i++) { 97 applyScale(ads[i]); 98 } 99 } 100 101 // Primary source of truth: GPT tells us the exact served size per slot. 102 window.googletag = window.googletag || { 103 cmd: [] 104 }; 105 window.googletag.cmd.push(function () { 106 window.googletag.pubads().addEventListener('slotRenderEnded', function (event) { 107 if (event.isEmpty || !Array.isArray(event.size)) { 108 return; 109 } 110 var el = document.getElementById(event.slot.getSlotElementId()); 111 if (!isAdContainer(el)) { 112 return; 113 } 114 var width = event.size[0]; 115 var height = event.size[1]; 116 // The iframe is appended right after the event fires. 117 window.requestAnimationFrame(function () { 118 track(el, width, height); 119 }); 120 }); 121 }); 122 123 // Fallback: catch any ad that rendered before the listener attached, using 124 // the iframe's own width/height attributes.
125 function initExisting() { 126 var els = document.querySelectorAll(AD_SELECTOR); 127 for (var i = 0; i < els.length; i++) { 128 // `.ad-main` also matches the blok's outer wrapper; skip it so we don't 129 // size against the wrong box or track its nested iframe twice. 130 if (!isAdContainer(els[i])) { 131 continue; 132 } 133 var iframe = els[i].querySelector('iframe'); 134 if (!iframe) { 135 continue; 136 } 137 track(els[i], parseInt(iframe.getAttribute('width'), 10), parseInt(iframe.getAttribute('height'), 10)); 138 } 139 } 140 if (document.readyState === 'loading') { 141 document.addEventListener('DOMContentLoaded', initExisting); 142 } else { 143 initExisting(); 144 } 145 // GPT renders asynchronously; sweep again shortly after load. 146 window.addEventListener('load', function () { 147 window.setTimeout(initExisting, 500); 148 }); 149 var resizeTimer; 150 window.addEventListener('resize', function () { 151 window.clearTimeout(resizeTimer); 152 resizeTimer = window.setTimeout(rescaleAll, 150); 153 }); 154})(); 155"use strict"; 156 157jQuery(document).ready(function ($) { 158 /** 159 * This snippet controls the back to top button. 160 */ 161 162 var backToTopButton = $("#backToTopButton"); 163 $(window).scroll(function () { 164 if ($(window).scrollTop() > 100) { 165 backToTopButton.addClass("show"); 166 } else { 167 backToTopButton.removeClass("show"); 168 } 169 }); 170 backToTopButton.on("click", function (e) { 171 e.preventDefault(); 172 $("html, body").animate({ 173 scrollTop: 0 174 }, 300); 175 }); 176}); 177"use strict"; 178 179jQuery(function filterButtons($) { 180 var loading_message_html = '<p>Loading...</p>'; 181 $('body').on('click', '.js-filter-button', function (e) { 182 e.preventDefault(); 183 var target_url = $(this).attr('href'); 184 var $container = $(this).closest('[data-filtered-query="post-list"]'); 185 var url_parameter = $(this).data('url-parameter'); 186 var value = $(this).data('value'); 187 188 // uncheck the corresponding filter checkbox 189 $('input[name="' + url_parameter + '[]"][value="' + value + '"]').prop('checked', false); 190 // add the loading message 191 192 $container.html(loading_message_html); 193 194 // execute the ajax request 195 196 $.get(target_url, {}, function (response) { 197 var $response = $(response); 198 var $response_container = $response.find('[data-filtered-query="post-list"]'); 199 $container.html($response_container.html()); 200 }); 201 }); 202}); 203"use strict"; 204 205jQuery(function filteredQuery($) { 206 var loading_message_html = '<p class="post-list-message">Loading...</p>'; 207 $('[data-filtered-query=container]').each(function (i, el) { 208 var $filtered_query_container = $(el); 209 var container_id = $filtered_query_container.attr('id'); 210 var ajax_url = $filtered_query_container.data('url'); 211 var $filter_inputs = $filtered_query_container.find('[data-filtered-query=filter]'); 212 var $post_list_containers = $filtered_query_container.find('[data-filtered-query=post-list]'); 213 var last_request_time; 214 215 // Todo: Debounce this function 216 $filter_inputs.on('change', runFilteredQuery); 217 218 // Todo: Check if pagination works the same with multiple containers 219 var topPagination = document.querySelector('[data-pagination="top"]'); 220 if (topPagination !== null) { 221 var topPaginationLocation = topPagination.getBoundingClientRect().top; 222 $post_list_containers.on('click', 'a.page-numbers', function (e) { 223 e.preventDefault(); 224 window.scrollTo({ 225 left: 0, 226 top: topPaginationLocation, 227 behavior: 'smooth' 228 }); 229 loadPaginationLink($(this).attr('href')); 230 }); 231 } 232 function runFilteredQuery() { 233 var request_time = Date.now(); 234 var filters_parameters = getFiltersParameters(); 235 console.log('getFiltersParameters', filters_parameters); 236 // Mark the last request time, so that we only use the latest response 237 // if the ajax requests come back out of order. 238 last_request_time = request_time; 239 addLoadingMessage(); 240 $.get(ajax_url, filters_parameters, function (response) { 241 // If the response is not the latest one, ignore it. 242 if (request_time !== last_request_time) { 243 return; 244 } 245 loadPostsFromResponse(response); 246 }); 247 } 248 function getFiltersParameters() { 249 var filters_parameters = {}; 250 $filter_inputs.each(function (i, el) { 251 var $el = $(el); 252 var name = $el.attr('name'); 253 var value = $el.val(); 254 if ($el.prop('checked')) { 255 switch ($el.attr('type')) { 256 case 'checkbox': 257 filters_parameters[name] = filters_parameters[name] || []; 258 filters_parameters[name].push(value); 259 break; 260 case 'radio': 261 filters_parameters[name] = value; 262 break; 263 } 264 } 265 }); 266 return filters_parameters; 267 } 268 function loadPostsFromResponse(response) { 269 var $response_wrapper = $(response); 270 var $response_container = $response_wrapper.find('#' + container_id); 271 var $response_post_list = $response_container.find('[data-filtered-query=post-list]'); 272 var $sidebar2 = $("#sidebar-2"); 273 $post_list_containers.each(function (index, el) { 274 var $this_post_list = $(el); 275 var $corresponding_post_list = $response_post_list.eq(index); 276 /** 277 * CMT-457: Remove second 'loading' message if there are fewer than 5 posts returned from selected filter. 278 * Author: Creed Development 279 */ 280 if ($corresponding_post_list.html() === undefined || $corresponding_post_list.html() == "") { 281 // hide the loading message; 282 $this_post_list.hide(); 283 $sidebar2.hide(); 284 } else { 285 $this_post_list.show(); 286 $sidebar2.show(); 287 } 288 $this_post_list.html($corresponding_post_list.html()); 289 }); 290 } 291 function loadPaginationLink(target_url) { 292 addLoadingMessage(); 293 $.get(target_url, {}, loadPostsFromResponse); 294 } 295 function addLoadingMessage() { 296 $post_list_containers.html(loading_message_html); 297 } 298 }); 299}); 300"use strict"; 301 302//== SHOW DROPDOWN IN MENU 303 304var navItems = document.querySelectorAll('#navMenu .nav-item');
305navItems.forEach(function (navItem) { 306 var subMenu = navItem.getElementsByClassName('sub-menu')[0]; 307 if (subMenu) { 308 navItem.addEventListener('click', function () { 309 var isVisible = subMenu.classList.contains('visible'); 310 if (isVisible) { 311 subMenu.classList.remove('visible'); 312 navItem.classList.remove('open'); 313 } else { 314 subMenu.classList.add('visible'); 315 navItem.classList.add('open'); 316 } 317 }); 318 navItem.addEventListener('mouseenter', function () { 319 subMenu.classList.add('visible'); 320 navItem.classList.add('open'); 321 }); 322 navItem.addEventListener('mouseleave', function () { 323 subMenu.classList.remove('visible'); 324 navItem.classList.remove('open'); 325 }); 326 } 327}); 328"use strict"; 329 330/** 331 * This snippet is for the side nav menu. 332 * It toggles the sub-menu when the nav item is clicked. 333 */ 334 335var sideNavItems = document.querySelectorAll('#mainNav .nav-item'); 336var prevItem = null; 337sideNavItems.forEach(function (navItem) { 338 var subMenu = navItem.getElementsByClassName('sub-menu')[0]; 339 if (subMenu) { 340 navItem.addEventListener('click', function () { 341 var isVisible = subMenu.classList.contains('visible'); 342 if (!prevItem) { 343 prevItem = navItem; 344 } else if (prevItem !== navItem) { 345 prevItem.classList.remove('open'); 346 prevItem.getElementsByClassName('sub-menu')[0].classList.remove('visible'); 347 prevItem = navItem; 348 } 349 if (isVisible) { 350 subMenu.classList.remove('visible'); 351 navItem.classList.remove('open'); 352 } else { 353 subMenu.classList.add('visible'); 354 navItem.classList.add('open'); 355 } 356 }); 357 } 358}); 359 360/** 361 * This snippet is for the mobile menu. 362 * It toggles the nav drawer menu when the burger menu is clicked. 363 */ 364 365var burgerMenu = document.getElementById("burgerMenu"); 366var navDrawer = document.getElementById("navDrawer"); 367var mainNav = document.getElementById("mainNav"); 368var menuShowing = false; 369var toggleMenu = function toggleMenu() { 370 navDrawer.classList.toggle("visible"); 371 burgerMenu.classList.toggle("close"); 372 menuShowing = !menuShowing; 373 if (prevItem) { 374 prevItem.classList.remove('open'); 375 prevItem.getElementsByClassName('sub-menu')[0].classList.remove('visible'); 376 } 377}; 378burgerMenu.onclick = function () { 379 return toggleMenu(); 380}; 381window.addEventListener("click", function (event) { 382 if (menuShowing && !burgerMenu.contains(event.target) && !mainNav.contains(event.target)) { 383 toggleMenu(); 384 } 385}); 386"use strict"; 387 388/** 389 * Transition the pull-quote if exists 390 */ 391var pullQuote = document.querySelector('.company-profile__pull-quote'); 392if (pullQuote) { 393 var observer = new IntersectionObserver(function (entries) { 394 entries.forEach(function (entry) { 395 if (entry.isIntersecting) { 396 pullQuote.classList.add('active'); 397 } 398 }); 399 }); 400 observer.observe(pullQuote); 401} 402"use strict"; 403 404/** 405 * Functionality for share post link 406 */ 407 408var shareCopy = document.querySelectorAll(".share-copy"); 409shareCopy.forEach(function (element) { 410 element.addEventListener("click", function () { 411 var copyText = element.getAttribute("data-tc-copyurl"); 412 var copyTarget = element.getAttribute("data-tc-copytarget"); 413 navigator.clipboard.writeText(copyText); 414 var myModal = new bootstrap.Modal(document.getElementById('copyModal-' + copyTarget)); 415 myModal.show(); 416 }); 417}); 418 419/** 420 * Toggle visibility of share links 421 */ 422 423var shareToggle = document.querySelectorAll(".share-links-button__toggle"); 424shareToggle.forEach(function (element) { 425 var visible = false; 426 var shareLinks = element.nextElementSibling; 427 element.addEventListener("click", function () { 428 if (visible) { 429 shareLinks.classList.remove('is-visible'); 430 visible = false; 431 } else { 432 shareLinks.classList.add('is-visible'); 433 visible = true; 434 } 435 }); 436}); 437"use strict"; 438 439/** 440 * Close the Subscribe CTA popup 441 */ 442 443var subscribeCTAStatus = sessionStorage.getItem('subscribeCTA'); 444var subscribeCTA = document.getElementById('subscribeCTA'); 445var closeSubscribeCTA = document.getElementById('closeSubscribeCTA'); 446if (subscribeCTA) { 447 if (subscribeCTAStatus !== 'hidden') { 448 setTimeout(function () { 449 subscribeCTA.classList.remove('hidden'); 450 }, 5000); 451 } 452 ; 453 closeSubscribeCTA.addEventListener('click', function () { 454 subscribeCTA.classList.add('hidden'); 455 sessionStorage.setItem('subscribeCTA', 'hidden'); 456 }); 457}
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.