More Featured
Webinars, Podcasts & Videos
From The Magazine
6 min read

Rethinking Stormwater Management Systems

Stormwater Management
Intensifying rainfall is pushing standard drainage systems past their limits. Here is how to audit your site’s stormwater drainage patterns and mitigate flood risk before the next major weather event.
4 min read

Smarter Restrooms: 5 Upgrades For Performance, Efficiency, And Design

Restroom Equipment 
These five products help facility executives improve durability, air quality, efficiency, and design while simplifying restroom upkeep.
6 min read

Your Security Perimeter Begins Before The Front Door 

Security Perimeter
Why today’s facilities need to rethink where active threat planning begins.
Latest Products & Services
4 min read

Smarter Restrooms: 5 Upgrades For Performance, Efficiency, And Design

Restroom Equipment 
These five products help facility executives improve durability, air quality, efficiency, and design while simplifying restroom upkeep.
4 min read

Doors And Locks: 5 Solutions Worth A Closer Look

Doors & Locks
Today’s door technologies help facility managers strengthen security, improve operational efficiency, support sustainability goals, and reduce installation and maintenance challenges.
5 min read

Deploying Autonomous Mowing 

Autonomous Mowing
An expert from RC Mowers discusses the latest trend in commercial grounds maintenance.
Request Product Info
Submit a Press Release
Webinars & Podcasts
Job Board

Receive the latest articles in your inbox

`; if (isWallpaper) { container.classList.remove('is-active'); iframe.addEventListener('load', () => { requestAnimationFrame(() => { container.classList.add('is-active'); }); }, { once: true }); } container.appendChild(iframe); container.setAttribute('data-active-id', String(spot.id)); container.style.display = 'block'; if (!isWallpaper) { requestAnimationFrame(() => { container.classList.add('is-active'); }); } } function clearSlot(container) { const activeId = container.getAttribute('data-active-id'); if (activeId) { loadedAdKeys.delete(container.id + ':' + activeId); } clearRefreshTimer(container.id); container.classList.remove('is-active'); container.style.display = 'none'; container.setAttribute('data-active-id', ''); container.innerHTML = ''; } function startRefreshTimer(slot, spot) { clearRefreshTimer(slot.containerId); const timer = setInterval(() => { if (!canRefreshAds()) return; const container = document.getElementById(slot.containerId); if (!container) { clearRefreshTimer(slot.containerId); return; } if (!window.matchMedia(spot.query).matches) { clearRefreshTimer(slot.containerId); clearSlot(container); return; } injectIframeAd(container, spot, { force: true }); }, REFRESH_MS); refreshTimers.set(slot.containerId, timer); } function processSlot(slot) { const container = document.getElementById(slot.containerId); if (!container) return; const match = slot.responsive.find( res => res.id && window.matchMedia(res.query).matches ); if (match) { injectIframeAd(container, match); container.style.display = 'block'; if (REFRESH_SLOT_IDS.has(slot.containerId)) { startRefreshTimer(slot, match); } else { clearRefreshTimer(slot.containerId); } } else { clearSlot(container); } } function checkSlots(mode = 'all') { adSlots.forEach(slot => { const isCritical = CRITICAL_SLOT_IDS.has(slot.containerId); const isScrollBound = SCROLL_SLOT_IDS.has(slot.containerId); if (mode === 'critical') { if (!isCritical) return; } else if (mode === 'delayed') { if (isCritical || isScrollBound) return; } else if (mode === 'scroll') { if (!isScrollBound) return; } processSlot(slot); }); } function debounce(fn, wait) { let t; return function() { clearTimeout(t); t = setTimeout(fn, wait); }; } /** * Handle viewport / breakpoint changes. * * Only process: * - critical slots * - scroll slots that have already activated * - delayed slots that IntersectionObserver already activated * * This prevents resize/orientation events from loading * every below-fold advertisement. */ const handleChange = debounce(() => { adSlots.forEach(slot => { const isCritical = CRITICAL_SLOT_IDS.has(slot.containerId); const isScrollBound = SCROLL_SLOT_IDS.has(slot.containerId); const isActivatedDelayed = activatedDelayedSlots.has(slot.containerId); /** * Critical ads may always react to * responsive breakpoint changes. */ if (isCritical) { processSlot(slot); return; } /** * Adhesion / scroll-bound placements should * only react after they have been activated. */ if (isScrollBound) { if (scrollStarted) { processSlot(slot); } return; } /** * Below-fold delayed ads should only react * after IntersectionObserver activates * that specific placement. */ if (isActivatedDelayed) { processSlot(slot); } }); }, 150); /** * Watch responsive breakpoints. */ function bindMediaListeners() { const allQueries = [ ...new Set( adSlots.flatMap( slot => slot.responsive.map( res => res.query ) ) ) ]; allQueries.forEach(query => { const mql = window.matchMedia(query); if ( typeof mql.addEventListener === 'function' ) { mql.addEventListener( 'change', handleChange ); } else if ( typeof mql.addListener === 'function' ) { mql.addListener( handleChange ); } }); window.addEventListener( 'resize', handleChange, { passive: true } ); window.addEventListener( 'orientationchange', handleChange, { passive: true } ); } /** * Observe non-critical, non-adhesion ads. * * Ads begin loading when they come within * approximately 800px of the viewport. */ function setupDelayedAds() { const delayedSlots = adSlots.filter(slot => { return ( !CRITICAL_SLOT_IDS.has( slot.containerId ) && !SCROLL_SLOT_IDS.has( slot.containerId ) ); }); if (!delayedSlots.length) { return; } /** * Fallback for browsers without * IntersectionObserver. */ if (!('IntersectionObserver' in window)) { setTimeout(() => { delayedSlots.forEach(slot => { activatedDelayedSlots.add( slot.containerId ); processSlot(slot); }); }, DELAY_MS); return; } const observer = new IntersectionObserver( entries => { entries.forEach(entry => { if (!entry.isIntersecting) { return; } const container = entry.target; const slot = delayedSlots.find( item => item.containerId === container.id ); if (!slot) { observer.unobserve( container ); return; } /** * Record that this specific * placement is now allowed * to load and respond to * breakpoint changes. */ activatedDelayedSlots.add( slot.containerId ); processSlot(slot); /** * Only trigger this placement * once. */ observer.unobserve( container ); }); }, { /** * Begin requesting the ad before * it actually enters the viewport. */ rootMargin: '800px 0px', threshold: 0 } ); delayedSlots.forEach(slot => { const container = document.getElementById( slot.containerId ); if (container) { observer.observe( container ); } }); } /** * Activate adhesion ads after the * visitor begins scrolling. */ function startScrollAds() { if (scrollStarted) { return; } scrollStarted = true; checkSlots('scroll'); } /** * Trigger adhesion inventory after * 200px of page scroll. */ function setupScrollTrigger() { const handleScroll = () => { if (window.scrollY >= 200) { startScrollAds(); window.removeEventListener( 'scroll', handleScroll ); } }; window.addEventListener( 'scroll', handleScroll, { passive: true } ); } /** * Pause adhesion refresh while the * page is hidden or does not have focus. */ function setupFocusVisibilityRefresh() { document.addEventListener( 'visibilitychange', () => { pageHasFocus = document.hasFocus(); if (!canRefreshAds()) { REFRESH_SLOT_IDS.forEach( clearRefreshTimer ); return; } adSlots.forEach(slot => { if ( !REFRESH_SLOT_IDS.has( slot.containerId ) ) { return; } processSlot(slot); }); } ); window.addEventListener( 'focus', () => { pageHasFocus = true; adSlots.forEach(slot => { if ( !REFRESH_SLOT_IDS.has( slot.containerId ) ) { return; } processSlot(slot); }); } ); window.addEventListener( 'blur', () => { pageHasFocus = false; REFRESH_SLOT_IDS.forEach( clearRefreshTimer ); } ); } /** * Initialize NUI advertisements. */ function init() { bindMediaListeners(); setupFocusVisibilityRefresh(); /** * Revenue-critical / above-fold inventory. */ checkSlots('critical'); /** * Adhesion inventory activates after * the visitor scrolls 200px. */ setupScrollTrigger(); /** * All remaining advertisements activate * only as they approach the viewport. */ if ( document.visibilityState === 'visible' ) { setupDelayedAds(); } else { document.addEventListener( 'visibilitychange', function onVisible() { if ( document.visibilityState !== 'visible' ) { return; } document.removeEventListener( 'visibilitychange', onVisible ); setupDelayedAds(); } ); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init, { once: true }); } else { init(); } })();