1jQuery(document).ready(function($) { 2 3 $('#listen-live').addClass('active'); 4 5 // 1. Cross-Origin Safe Reference to Top Window Context 6 var safeTopWindow; 7 try { 8 var testTop = window.top.document; 9 safeTopWindow = window.top; 10 } catch (e) { 11 safeTopWindow = window; 12 } 13 14 // 2. Station Metadata Map (Uses PNG/JPG URLs strictly for iOS artwork compatibility) 15 var stationData = { 16 'persistent-KJJQ-AM-The-Ranch': { 17 url: 'https://live.amperwave.net/direct/brookings-kjjqammp3-imc', 18 title: 'AM 910 The Ranch', 19 artist: '910 AM - Brookings Radio', 20 logo: 'https://brookingsradio.com/wp-content/uploads/2026/07/cropped-Brookings-Radio-Favicon-192x192.png' // PNG Fallback for SVG 21 }, 22 'persistent-KBRK-AM-1430': { 23 url: 'https://live.amperwave.net/direct/brookings-kbrkfmmp3-imc', 24 title: 'KBRK Traditional', 25 artist: '1430 AM - Brookings Radio', 26 logo: 'https://brookingsradio.com/wp-content/uploads/2026/07/cropped-Brookings-Radio-Favicon-192x192.png' // PNG Fallback for SVG 27 }, 28 'persistent-B-93.7-FM': { 29 url: 'https://live.amperwave.net/direct/brookings-kbrkammp3-imc' 30 , 31 title: 'KBRK B93.7', 32 artist: '93.7 FM - Brookings Radio', 33 logo: 'https://brookingsradio.com/wp-content/uploads/2026/07/new-b-logo.png' 34 }, 35 'persistent-102.3-FM': { 36 url: 'https://live.amperwave.net/direct/brookings-kkqqfmmp3-imc', 37 title: 'KKQQ KCountry', 38 artist: '102.3 FM - Brookings Radio', 39 logo: 'https://brookingsradio.com/wp-content/uploads/2026/07/KKQQ-Logo.png' 40 }, 41 'persistent-107.1-The-Hawk': { 42 url: 'https://live.amperwave.net/direct/brookings-kdbxfmmp3-imc', 43 title: 'KDBX The Hawk', 44 artist: '107.1 FM - Brookings Radio', 45 logo: 'https://brookingsradio.com/wp-content/uploads/2026/07/hawk-logo.jpg' 46 } 47 }; 48 49 // 3. Global Audio State on safeTopWindow 50 safeTopWindow.RadioStations = safeTopWindow.RadioStations || { 51 activeStationKey: null, 52 audioInstance: null, 53 isBound: false 54 }; 55 56 // Helper to sync UI icon states on the widget 57 function setCardUIState(container, isPlaying) { 58 if (!container) return; 59 60 var $container = $(container); 61 if (isPlaying) { 62 $container.addClass('is-playing playing paused-state-off'); 63 $container.find('.cp-play-button').addClass('cp-pause-button').removeClass('cp-play-button'); 64 } else { 65 $container.removeClass('is-playing playing paused-state-off'); 66 $container.find('.cp-pause-button').addClass('cp-play-button').removeClass('cp-pause-button'); 67 } 68 } 69 70 // Helper to explicitly force iOS / Android Lock Screen Metadata 71 function updateLockScreenMetadata(station) { 72 if ('mediaSession' in navigator) { 73 try { 74 navigator.mediaSession.metadata = new MediaMetadata({ 75 title: station.title, 76 artist: station.artist, 77 album: 'Brookings Radio Live', 78 artwork: [ 79 { src: station.logo, sizes: '192x192', type: 'image/png' }, 80 { src: station.logo, sizes: '512x512', type: 'image/png' } 81 ] 82 }); 83 84 // Bind hardware play/pause actions on iOS lock screen 85 navigator.mediaSession.setActionHandler('play', function() { 86 if (safeTopWindow.RadioStations.audioInstance) { 87 safeTopWindow.RadioStations.audioInstance.play(); 88 } 89 }); 90 navigator.mediaSession.setActionHandler('pause', function() { 91 stopActiveStream(); 92 }); 93 } catch (err) { 94 console.warn('MediaSession initialization notice:', err); 95 } 96 } 97 } 98 99 // Helper to completely stop and destroy the current audio stream 100 function stopActiveStream() { 101 var state = safeTopWindow.RadioStations; 102 103 if (state.audioInstance) { 104 state.audioInstance.pause(); 105 state.audioInstance.removeAttribute('src'); // Drops network connection 106 if (typeof state.audioInstance.load === 'function') { 107 state.audioInstance.load(); 108 } 109 state.audioInstance = null; 110 } 111 112 if (state.activeStationKey) {
113 var topDocNode = safeTopWindow.document; 114 var prevContainer = topDocNode.getElementById(state.activeStationKey); 115 if (prevContainer) { 116 setCardUIState(prevContainer, false); 117 } 118 state.activeStationKey = null; 119 } 120 121 // Clear lock screen metadata on stop 122 if ('mediaSession' in navigator) { 123 navigator.mediaSession.metadata = null; 124 } 125 } 126 127 // 4. Mobile & Desktop Unified Touch/Click Handler 128 function bindExclusivePlayback() { 129 var topDocNode = safeTopWindow.document; 130 131 function handleStationTap(e) { 132 var currentContainer = e.target.closest('#ajaxpress-persist div[id^="persistent-"]'); 133 if (!currentContainer) return; 134 135 e.preventDefault(); 136 e.stopPropagation(); 137 138 var clickedStationId = currentContainer.id; 139 var station = stationData[clickedStationId]; 140 var state = safeTopWindow.RadioStations; 141 142 if (!station) return; 143 144 // IF USER CLICKED THE CURRENTLY PLAYING STATION -> PAUSE IT 145 if (state.activeStationKey === clickedStationId) { 146 stopActiveStream(); 147 return; 148 } 149 150 // IF A DIFFERENT STATION WAS PLAYING -> STOP IT FIRST 151 stopActiveStream(); 152 153 // START THE NEW STREAM USING A CLEAN AUDIO INSTANCE 154 var newAudio = new Audio(); 155 newAudio.preload = 'none'; 156 newAudio.src = station.url; 157 158 // Bind metadata update to audio play event for iOS Safari 159 newAudio.addEventListener('play', function() { 160 updateLockScreenMetadata(station); 161 }); 162 163 newAudio.play().then(function() { 164 state.audioInstance = newAudio; 165 state.activeStationKey = clickedStationId; 166 setCardUIState(currentContainer, true); 167 updateLockScreenMetadata(station); 168 }).catch(function(err) { 169 console.error('Stream playback error:', err); 170 }); 171 } 172 173 var eventType = ('ontouchstart' in window) ? 'touchstart' : 'click'; 174 topDocNode.addEventListener(eventType, handleStationTap, true); 175 } 176 177 if (!safeTopWindow.RadioStations.isBound) { 178 bindExclusivePlayback(); 179 safeTopWindow.RadioStations.isBound = true; 180 } 181 182 // 5. Persistent Drawer Toggle Handler 183 $(document).off('click.listenLiveToggle').on('click.listenLiveToggle', '#listen-live a.elementor-button, #listen-live', function(e) { 184 e.preventDefault(); 185 e.stopPropagation(); 186 187 var topDoc = safeTopWindow.document; 188 var $persistEl = $(topDoc).find('#ajaxpress-persist'); 189 190 if ($persistEl.length) { 191 $(this).toggleClass('active'); 192 if ($(this).hasClass('active')) { 193 $(this).find('span.elementor-button-text').html('Close Player'); 194 } else { 195 $(this).find('span.elementor-button-text').html('Listen Live'); 196 } 197 $persistEl.toggleClass('active'); 198 topDoc.body.classList.toggle('player-active'); 199 } 200 }); 201 202}); 203 204
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.