1/** 2 * Google Reach Measurement Error Handler for Maritime Executive 3 * 4 * Handles errors from Google's reach and frequency measurement system 5 * and prevents console spam from failed reach_worklet.js requests 6 */ 7 8(function () { 9 'use strict'; 10 11 // Configuration 12 const config = { 13 suppressReachErrors: true, 14 logErrorsToConsole: false, 15 maxErrorsLogged: 5, 16 errorLogInterval: 60000 // 1 minute 17 }; 18 19 // Track errors to prevent spam 20 let errorCount = 0; 21 let lastErrorTime = 0; 22 let errorBuffer = []; 23 24 /** 25 * Handle Google Reach measurement errors 26 */ 27 function handleReachError(error, url) { 28 const now = Date.now(); 29 30 // Add to error buffer 31 errorBuffer.push({ 32 timestamp: now, 33 url: url, 34 error: error 35 }); 36 37 // Clean old errors from buffer (keep last hour) 38 const oneHourAgo = now - (60 * 60 * 1000); 39 errorBuffer = errorBuffer.filter(e => e.timestamp > oneHourAgo); 40 41 // Only log periodically to avoid spam 42 if (now - lastErrorTime > config.errorLogInterval) { 43 if (config.logErrorsToConsole && errorBuffer.length > 0) { 44 console.group('[Google Reach] Error Summary'); 45 console.warn(`${errorBuffer.length} reach measurement errors in the last hour`); 46 console.log('Most recent error:', errorBuffer[errorBuffer.length - 1]); 47 console.groupEnd(); 48 } 49 lastErrorTime = now; 50 } 51 } 52 53 /** 54 * Monkey patch fetch to catch reach worklet errors 55 */ 56 function patchFetch() { 57 if (typeof window.fetch !== 'function') return; 58 59 const originalFetch = window.fetch; 60 61 window.fetch = function (...args) { 62 const url = args[0]; 63 64 return originalFetch.apply(this, args) 65 .catch(error => { 66 // Check if this is a reach measurement error 67 if (typeof url === 'string' && 68 (url.includes('googletagservices.com/agrp') || 69 url.includes('reach_worklet') || 70 url.includes('model_person_country_code'))) { 71 72 handleReachError(error, url); 73 74 if (config.suppressReachErrors) { 75 // Return a resolved promise to prevent further error propagation 76 return Promise.resolve(new Response('{}', { 77 status: 200, 78 statusText: 'OK', 79 headers: { 'Content-Type': 'application/json' } 80 })); 81 } 82 } 83 84 // Re-throw other errors 85 throw error; 86 }); 87 }; 88 } 89 90 /** 91 * Patch XMLHttpRequest to catch reach worklet errors 92 */ 93 function patchXHR() { 94 const originalOpen = XMLHttpRequest.prototype.open; 95 const originalSend = XMLHttpRequest.prototype.send; 96 97 XMLHttpRequest.prototype.open = function (method, url, ...args) { 98 this._url = url; 99 return originalOpen.call(this, method, url, ...args); 100 }; 101 102 XMLHttpRequest.prototype.send = function (data) { 103 const xhr = this; 104 const originalOnError = xhr.onerror; 105 const originalOnLoad = xhr.onload; 106 107 // Handle error events 108 xhr.onerror = function (event) { 109 if (xhr._url && 110 (xhr._url.includes('googletagservices.com/agrp') || 111 xhr._url.includes('reach_worklet') || 112 xhr._url.includes('model_person_country_code'))) { 113 114 handleReachError(event, xhr._url); 115 116 if (config.suppressReachErrors) { 117 // Prevent error from propagating 118 event.stopPropagation(); 119 event.preventDefault(); 120 return; 121 } 122 } 123 124 if (originalOnError) { 125 originalOnError.call(xhr, event); 126 } 127 }; 128 129 // Handle load events with error status 130 xhr.onload = function (event) { 131 if (xhr.status >= 400 && xhr._url && 132 (xhr._url.includes('googletagservices.com/agrp') || 133 xhr._url.includes('reach_worklet') || 134 xhr._url.includes('model_person_country_code'))) { 135 136 handleReachError(`HTTP ${xhr.status}`, xhr._url); 137 } 138 139 if (originalOnLoad) {
140 originalOnLoad.call(xhr, event); 141 } 142 }; 143 144 return originalSend.call(this, data); 145 }; 146 } 147 148 /** 149 * Override console.error to filter reach errors 150 */ 151 function patchConsoleError() { 152 if (!config.suppressReachErrors) return; 153 154 const originalError = console.error; 155 156 console.error = function (...args) { 157 const message = args.join(' '); 158 159 // Check if this is a reach measurement error 160 if (message.includes('googletagservices.com/agrp') || 161 message.includes('reach_worklet') || 162 message.includes('model_person_country_code') || 163 message.includes('Internal Server Error')) { 164 165 // Count the error but don't log it 166 errorCount++; 167 return; 168 } 169 170 // Log other errors normally 171 originalError.apply(console, args); 172 }; 173 } 174 175 /** 176 * Disable Google Reach measurement if requested 177 */ 178 function disableReachMeasurement() { 179 // This needs to be done early, before Google Tag loads 180 if (window.googletag) { 181 googletag.cmd.push(function () { 182 try { 183 // Disable reach measurement 184 googletag.pubads().setPrivacySettings({ 185 restrictDataProcessing: true 186 }); 187 188 // Disable personalized ads (reduces reach tracking) 189 googletag.pubads().setRequestNonPersonalizedAds(1); 190 191 console.log('[Google Reach] Measurement disabled'); 192 } catch (e) { 193 console.warn('[Google Reach] Could not disable measurement:', e.message); 194 } 195 }); 196 } 197 } 198 199 /** 200 * Add global error handler for unhandled promise rejections 201 */ 202 function addGlobalErrorHandler() { 203 window.addEventListener('unhandledrejection', function (event) { 204 const error = event.reason; 205 const message = error ? error.toString() : ''; 206 207 if (message.includes('googletagservices.com/agrp') || 208 message.includes('reach_worklet') || 209 message.includes('model_person_country_code')) { 210 211 handleReachError(error, 'promise_rejection'); 212 213 if (config.suppressReachErrors) { 214 event.preventDefault(); 215 } 216 } 217 }); 218 219 window.addEventListener('error', function (event) { 220 const message = event.message || ''; 221 const filename = event.filename || ''; 222 223 if (message.includes('reach_worklet') || 224 filename.includes('reach_worklet') || 225 message.includes('googletagservices.com/agrp')) { 226 227 handleReachError(event.error, filename); 228 229 if (config.suppressReachErrors) { 230 event.preventDefault(); 231 } 232 } 233 }); 234 } 235 236 /** 237 * Initialize error handling 238 */ 239 function init() { 240 console.log('[Google Reach] Error handler initializing...'); 241 242 // Apply patches 243 patchFetch(); 244 patchXHR(); 245 patchConsoleError(); 246 addGlobalErrorHandler(); 247 248 // Optionally disable reach measurement 249 // disableReachMeasurement(); 250 251 console.log('[Google Reach] Error handler initialized'); 252 } 253 254 /** 255 * Expose utilities for debugging 256 */ 257 window.GoogleReachHandler = { 258 getErrorCount: () => errorCount, 259 getErrorBuffer: () => [...errorBuffer], 260 clearErrors: () => { 261 errorCount = 0; 262 errorBuffer = []; 263 }, 264 enableLogging: () => { 265 config.logErrorsToConsole = true; 266 }, 267 disableLogging: () => { 268 config.logErrorsToConsole = false; 269 }, 270 suppressErrors: () => { 271 config.suppressReachErrors = true; 272 }, 273 allowErrors: () => { 274 config.suppressReachErrors = false;
275 }, 276 disableReachMeasurement: disableReachMeasurement, 277 config: config 278 }; 279 280 // Initialize immediately 281 init(); 282 283})();
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.