1/** 2 * WPB-7055: On-air show JS library 3 * Handles client-side show switching for cached pages. 4 * Debug: add ?wpb_onair_debug=1 to URL for console logs. 5 * Force update: add ?js_update_check=1 to force JS to update show even if cached show matches. 6 */ 7var WPBOnAirShow = (function() { 8 var LOG_PREFIX = 'ON AIR SHOW JS : '; 9 var DEBUG = (window.location.search.indexOf('wpb_onair_debug') !== -1); 10 var JS_UPDATE_CHECK = (window.location.search.indexOf('js_update_check') !== -1); 11 12 function log_onair_show_js(msg) { if (DEBUG) console.log(LOG_PREFIX + msg); } 13 14 function getLocalTime(gmtOffset) { 15 var now = new Date(); 16 var utc = now.getTime() + (now.getTimezoneOffset() * 60000); 17 return new Date(utc + (gmtOffset * 3600000)); 18 } 19 20 // WPB-7055: Time formatting - change this function to affect all time displays across all sites 21 function formatTime(timeStr) { 22 if (!timeStr) return ''; 23 var parts = timeStr.split(':'); 24 var hour = parseInt(parts[0]); 25 var min = parts[1] ? parts[1] : '00'; 26 if (min.length === 1) min = '0' + min; 27 var ampm = hour >= 12 ? 'PM' : 'AM'; 28 hour = hour % 12; 29 if (hour === 0) hour = 12; 30 return hour + ':' + min + ' ' + ampm; 31 } 32 33 // WPB-7055: Build schedule display text - change this function to affect schedule format 34 function formatScheduleText(show, existingText) { 35 if (show._matchedSlot) { 36 if (existingText && (existingText.indexOf('-') !== -1 || existingText.indexOf('&') !== -1)) { 37 var daysPart = show.days_text || ''; 38 var startF = formatTime(show._matchedSlot.start); 39 var endF = formatTime(show._matchedSlot.end); 40 return (daysPart ? daysPart + ': ' : '') + startF + ' - ' + endF; 41 } else { 42 return formatTime(show._matchedSlot.start); 43 } 44 } 45 var schedText = show.schedule_text || ''; 46 if (typeof schedText === 'object') { 47 var parts = []; 48 for (var day in schedText) { 49 if (schedText.hasOwnProperty(day)) { 50 parts.push(day + ': ' + (Array.isArray(schedText[day]) ? schedText[day].join(' & ') : schedText[day])); 51 } 52 } 53 schedText = parts.join(', '); 54 } 55 return schedText; 56 } 57 58 function findCurrentShow(data) { 59 var siteTime = getLocalTime(data.gmt_offset); 60 var currentDay = siteTime.getDay(); 61 var previousDay = (currentDay + 6) % 7; 62 var currentTimeInt = siteTime.getHours() * 100 + siteTime.getMinutes(); 63 var todayStr = siteTime.getFullYear() + '-' + ('0' + (siteTime.getMonth() + 1)).slice(-2) + '-' + ('0' + siteTime.getDate()).slice(-2); 64 log_onair_show_js('Instance ' + data.instance + ' | Site time: day=' + currentDay + ', time=' + siteTime.getHours() + ':' + (siteTime.getMinutes() < 10 ? '0' : '') + siteTime.getMinutes() + ', timeInt=' + currentTimeInt + ', date=' + todayStr); 65 66 // Priority 1: Check one-time shows first (special events, holiday broadcasts) 67 for (var i = 0; i < data.shows.length; i++) { 68 var show = data.shows[i]; 69 if (!show.onetime_slots || !show.onetime_slots.length) continue; 70 for (var j = 0; j < show.onetime_slots.length; j++) { 71 var slot = show.onetime_slots[j]; 72 if (slot.date !== todayStr) continue; 73 var startParts = slot.start.split(':'); var endParts = slot.end.split(':'); 74 var startInt = parseInt(startParts[0]) * 100 + parseInt(startParts[1]); 75 var endInt = parseInt(endParts[0]) * 100 + parseInt(endParts[1]); 76 var isOvernight = (endInt <= startInt); 77 if (isOvernight) { 78 if (currentTimeInt >= startInt) { show._matchedSlot = slot; log_onair_show_js('Instance ' + data.instance + ' | Found one-time overnight show: "' + show.title + '" on ' + slot.date); return show; } 79 } else { 80 if (currentTimeInt >= startInt && currentTimeInt < endInt) { show._matchedSlot = slot; log_onair_show_js('Instance ' + data.instance + ' | Found one-time show: "' + show.title + '" on ' + slot.date); return show; } 81 } 82 } 83 } 84 85 // Also check if a one-time overnight show started yesterday and spills into today 86 var yesterday = new Date(siteTime); yesterday.setDate(yesterday.getDate() - 1); 87 var yesterdayStr = yesterday.getFullYear() + '-' + ('0' + (yesterday.getMonth() + 1)).slice(-2) + '-' + ('0' + yesterday.getDate()).slice(-2); 88 for (var i = 0; i < data.shows.length; i++) { 89 var show = data.shows[i]; 90 if (!show.onetime_slots || !show.onetime_slots.length) continue; 91 for (var j = 0; j < show.onetime_slots.length; j++) { 92 var slot = show.onetime_slots[j]; 93 if (slot.date !== yesterdayStr) continue; 94 var startParts = slot.start.split(':'); var endParts = slot.end.split(':'); 95 var startInt = parseInt(startParts[0]) * 100 + parseInt(startParts[1]); 96 var endInt = parseInt(endParts[0]) * 100 + parseInt(endParts[1]); 97 if (endInt <= startInt && currentTimeInt < endInt) { 98 show._matchedSlot = slot; log_onair_show_js('Instance ' + data.instance + ' | Found one-time overnight show (spill from yesterday): "' + show.title + '" on ' + slot.date); return show; 99 } 100 } 101 } 102 103 // Priority 2: Check recurring shows 104 for (var i = 0; i < data.shows.length; i++) { 105 var show = data.shows[i]; 106 for (var j = 0; j < show.slots.length; j++) { 107 var slot = show.slots[j]; 108 var startParts = slot.start.split(':'); var endParts = slot.end.split(':'); 109 var startInt = parseInt(startParts[0]) * 100 + parseInt(startParts[1]); 110 var endInt = parseInt(endParts[0]) * 100 + parseInt(endParts[1]); 111 var isOvernight = (endInt <= startInt); 112 if (isOvernight) { 113 if ((slot.day === currentDay && currentTimeInt >= startInt) || (slot.day === previousDay && currentTimeInt < endInt)) { 114 show._matchedSlot = slot; log_onair_show_js('Instance ' + data.instance + ' | Found overnight show: "' + show.title + '" slot ' + slot.start + '-' + slot.end); return show; 115 } 116 } else { 117 if (slot.day === currentDay && currentTimeInt >= startInt && currentTimeInt < endInt) { 118 show._matchedSlot = slot; log_onair_show_js('Instance ' + data.instance + ' | Found show: "' + show.title + '" slot ' + slot.start + '-' + slot.end); return show; 119 } 120 } 121 } 122 } 123 124 // Fallback: default show 125 if (data.default_show_id) { 126 for (var i = 0; i < data.shows.length; i++) { 127 if (data.shows[i].id === data.default_show_id) { log_onair_show_js('Instance ' + data.instance + ' | Using default show: "' + data.shows[i].title + '"'); return data.shows[i]; } 128 } 129 } 130 131 // Fallback: next upcoming show 132 var nextResult = findNextShow(data, currentDay, currentTimeInt); 133 if (nextResult) { log_onair_show_js('Instance ' + data.instance + ' | Using next show: "' + nextResult.title + '"'); } 134 else { log_onair_show_js('Instance ' + data.instance + ' | No show found at all'); } 135 return nextResult; 136 }//End of func - findCurrentShow 137 138 function findNextShow(data, currentDay, currentTimeInt) { 139 var nextShow = null, nextStart = 99999, nextSlot = null; 140 for (var i = 0; i < data.shows.length; i++) { 141 for (var j = 0; j < data.shows[i].slots.length; j++) { 142 var slot = data.shows[i].slots[j]; 143 if (slot.day === currentDay) { 144 var si = parseInt(slot.start.split(':')[0]) * 100 + parseInt(slot.start.split(':')[1]); 145 if (si > currentTimeInt && si < nextStart) { nextStart = si; nextShow = data.shows[i]; nextSlot = slot; } 146 } 147 } 148 } 149 if (nextShow) { nextShow._matchedSlot = nextSlot; return nextShow; } 150 var tomorrow = (currentDay + 1) % 7; nextStart = 99999; 151 for (var i = 0; i < data.shows.length; i++) { 152 for (var j = 0; j < data.shows[i].slots.length; j++) { 153 var slot = data.shows[i].slots[j]; 154 if (slot.day === tomorrow) { 155 var si = parseInt(slot.start.split(':')[0]) * 100 + parseInt(slot.start.split(':')[1]); 156 if (si < nextStart) { nextStart = si; nextShow = data.shows[i]; nextSlot = slot; } 157 } 158 } 159 } 160 if (nextShow) { nextShow._matchedSlot = nextSlot; } 161 return nextShow; 162 } 163 164 function getContainer(data) { 165 var c = document.querySelector('.onair_ele_widget[data-onair-instance="' + data.instance + '"] .elementor-template'); 166 if (!c) { c = document.querySelector('.onair_ele_widget[data-onair-instance="' + data.instance + '"] [data-elementor-type]'); } 167 if (!c) { c = document.querySelector('.onair_ele_widget .elementor-template'); } 168 if (!c) { c = document.querySelector('.onair_ele_widget [data-elementor-type]'); } 169 log_onair_show_js('Instance ' + data.instance + ' | Container found: ' + (c ? 'YES' : 'NO')); 170 return c; 171 } 172 173 // WPB: Centralized title element lookup 174 function getOnAirShowTitle(container) { 175 if (!container) return null; 176 var titleEl = container.querySelector('.onecms-on-air-show-ele-title .elementor-heading-title'); 177 if (!titleEl) { titleEl = container.querySelector('.elementor-widget-theme-post-title .elementor-heading-title'); } 178 if (!titleEl) { 179 var headings = container.querySelectorAll('.elementor-widget-heading .elementor-heading-title'); 180 if (headings.length >= 2) { titleEl = headings[1]; } 181 } 182 return titleEl; 183 } 184 185 // WPB: Centralized image element lookup 186 function getOnAirShowImage(container) { 187 if (!container) return null; 188 var imgEl = container.querySelector('.onecms-on-air-show-ele-image img'); 189 if (imgEl) { 190 var btnParent = imgEl.closest('.schedule-one-play, .schedule-one-pause'); 191 if (!btnParent) return imgEl; 192 } 193 var imgEls = container.querySelectorAll('.elementor-widget-image img'); 194 for (var i = 0; i < imgEls.length; i++) { 195 var parent = imgEls[i].closest('.schedule-one-play, .schedule-one-pause'); 196 if (!parent) { return imgEls[i]; } 197 } 198 return null; 199 } 200 201 // WPB: Find the show photo column (background-image column for listen-live templates) 202 function getOnAirShowBgColumn(container) { 203 if (!container) return null; 204 var cols = container.querySelectorAll('.elementor-column[data-settings]'); 205 for (var i = 0; i < cols.length; i++) {
206 try { 207 var settings = JSON.parse(cols[i].getAttribute('data-settings')); 208 if (settings && settings.background_background === 'classic') { 209 var wrap = cols[i].querySelector('.elementor-widget-wrap'); 210 if (wrap && !wrap.classList.contains('elementor-element-populated')) { return cols[i]; } 211 } 212 } catch(e) {} 213 } 214 return null; 215 } 216 217 // WPB: Centralized schedule/timing element lookup 218 function getOnAirShowSchedule(container) { 219 if (!container) return null; 220 var schedEl = container.querySelector('.onecms-on-air-show-ele-time-slot .elementor-heading-title'); 221 if (schedEl) return schedEl; 222 var scheduleEls = container.querySelectorAll('.elementor-widget-heading .elementor-heading-title'); 223 for (var s = 0; s < scheduleEls.length; s++) { 224 if (/\d{1,2}(:\d{2})?\s*(AM|PM|am|pm)/i.test(scheduleEls[s].textContent)) { return scheduleEls[s]; } 225 } 226 return null; 227 } 228 229 function shortenDaysText(daysText) { 230 if (!daysText) return ''; 231 var dayMap = {'Monday':'Mon','Tuesday':'Tue','Wednesday':'Wed','Thursday':'Thu','Friday':'Fri','Saturday':'Sat','Sunday':'Sun'}; 232 var orderedDays = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']; 233 var parts = daysText.split(','); var days = []; 234 for (var i = 0; i < parts.length; i++) { var d = parts[i].trim(); if (d) days.push(d); } 235 if (days.length === 0) return ''; 236 if (days.length === 1) return dayMap[days[0]] || days[0]; 237 var indices = []; 238 for (var i = 0; i < days.length; i++) { var idx = orderedDays.indexOf(days[i]); if (idx !== -1) indices.push(idx); } 239 indices.sort(function(a,b){return a-b;}); 240 var isConsecutive = true; 241 for (var i = 1; i < indices.length; i++) { if (indices[i] !== indices[i-1] + 1) { isConsecutive = false; break; } } 242 if (isConsecutive && days.length > 2) { 243 return (dayMap[orderedDays[indices[0]]] || orderedDays[indices[0]]) + ' - ' + (dayMap[orderedDays[indices[indices.length-1]]] || orderedDays[indices[indices.length-1]]); 244 } 245 var shortened = []; 246 for (var i = 0; i < days.length; i++) { shortened.push(dayMap[days[i]] || days[i]); } 247 return shortened.join(', '); 248 } 249 250 function buildScheduleText(show, scheduleFormat) { 251 if (!show._matchedSlot) return ''; 252 var startF = formatTime(show._matchedSlot.start); var endF = formatTime(show._matchedSlot.end); 253 if (scheduleFormat === 'full') { 254 var daySlots = []; 255 for (var s = 0; s < show.slots.length; s++) { 256 if (show.slots[s].day === show._matchedSlot.day) { 257 daySlots.push(formatTime(show.slots[s].start) + ' - ' + formatTime(show.slots[s].end)); 258 } 259 } 260 if (daySlots.length === 0) { daySlots.push(startF + ' - ' + endF); } 261 var daysShort = shortenDaysText(show.days_text); 262 return (daysShort ? daysShort + ': ' : '') + daySlots.join(' & '); 263 } else { 264 return startF + ' - ' + endF; 265 } 266 } 267 268 function updateWidget(data, show) { 269 if (!show) return; 270 var container = getContainer(data); if (!container) return; 271 log_onair_show_js('Instance ' + data.instance + ' | Updating widget to: "' + show.title + '"'); 272 273 // Update title 274 var titleEl = getOnAirShowTitle(container); 275 if (titleEl) { 276 var titleLink = titleEl.querySelector('a') || titleEl.closest('a') || titleEl.parentElement.closest('a'); 277 if (titleLink) { 278 titleLink.textContent = show.title; 279 if (show.permalink) { titleLink.href = show.permalink; } 280 } else { 281 titleEl.textContent = show.title; 282 } 283 284 if (JS_UPDATE_CHECK) { 285 titleEl.textContent = titleEl.textContent + ' - Test Developer'; 286 } 287 } 288 289 // Update schedule/time slot 290 var scheduleEl = getOnAirShowSchedule(container); 291 if (scheduleEl && show._matchedSlot) { 292 var existingText = scheduleEl.textContent.trim(); 293 if (!data._scheduleFormat) { 294 var hasDays = /^[A-Za-z]/.test(existingText); 295 var hasAmpersand = existingText.indexOf('&') !== -1; 296 data._scheduleFormat = (hasDays || hasAmpersand) ? 'full' : 'simple'; 297 log_onair_show_js('Instance ' + data.instance + ' | Detected schedule format: ' + data._scheduleFormat); 298 } 299 // Prefer show excerpt if it contains timing info 300 var fmtText = ''; 301 if (show.excerpt && /\d{1,2}(:\d{2})?\s*(AM|PM)/i.test(show.excerpt)) { 302 fmtText = show.excerpt; 303 log_onair_show_js('Instance ' + data.instance + ' | Using excerpt for schedule: "' + fmtText + '"'); 304 } else { 305 fmtText = buildScheduleText(show, data._scheduleFormat); 306 } 307 if (fmtText && existingText !== fmtText) { 308 log_onair_show_js('Instance ' + data.instance + ' | Schedule updating: "' + existingText + '" -> "' + fmtText + '"'); 309 scheduleEl.textContent = fmtText; 310 } 311 } 312 313 // Update show image - only via custom class or background-image column 314 if (show.image) { 315 var imgEl = container.querySelector('.onecms-on-air-show-ele-image:not(.schedule-one-play):not(.schedule-one-pause) img'); 316 if (imgEl) { imgEl.src = show.image; imgEl.alt = show.title; } 317 else { 318 var bgCol = getOnAirShowBgColumn(container); 319 if (bgCol) { 320 var wrap = bgCol.querySelector('.elementor-widget-wrap'); 321 if (wrap) { wrap.style.backgroundImage = 'url(' + show.image + ')'; wrap.style.backgroundSize = 'cover'; wrap.style.backgroundPosition = 'center center'; log_onair_show_js('Instance ' + data.instance + ' | Updated show photo (bg-image column)'); } 322 } 323 } 324 } 325 }// End of function - updateWidget 326 327 function init(data) { 328 log_onair_show_js('Instance ' + data.instance + ' | Init, shows: ' + data.shows.length + ', gmt_offset: ' + data.gmt_offset); 329 var currentShowId = null; 330 331 function check() { 332 var show = findCurrentShow(data); if (!show) return; 333 if (show.id !== currentShowId) { 334 log_onair_show_js('Instance ' + data.instance + ' | Show CHANGED to "' + show.title + '"'); 335 updateWidget(data, show); currentShowId = show.id; 336 } 337 } 338 339 function start() { 340 var show = findCurrentShow(data); if (!show) return; 341 currentShowId = show.id; 342 var container = getContainer(data); 343 if (container) { 344 var needsUpdate = false;
345 var titleEl = getOnAirShowTitle(container); 346 if (titleEl && titleEl.textContent.trim() !== show.title.trim()) { needsUpdate = true; log_onair_show_js('Instance ' + data.instance + ' | Cached page stale, updating to "' + show.title + '"'); } 347 var imgEl = getOnAirShowImage(container); 348 if (imgEl && show.image) { 349 var imgSrc = imgEl.getAttribute('src') || ''; 350 if (imgSrc.indexOf('placeholder') !== -1 || imgSrc === '') { needsUpdate = true; log_onair_show_js('Instance ' + data.instance + ' | Image is placeholder, fixing'); } 351 } 352 if (JS_UPDATE_CHECK) { needsUpdate = true; console.log(LOG_PREFIX + 'Instance ' + data.instance + ' | Developer check needsUpdate on page load forcefully to check.....'); } 353 if (needsUpdate) { updateWidget(data, show); } 354 else { log_onair_show_js('Instance ' + data.instance + ' | No Update on Initial Load............'); } 355 } 356 var intervalId = setInterval(function() { 357 var container = getContainer(data); 358 if (!container) { 359 clearInterval(intervalId); 360 return; 361 } 362 check(); 363 }, 60000); 364 } 365 366 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', start); } else { start(); } 367 }//End of func - init 368 369 return { init: init }; 370})();
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.