1/* Microsoft browser contract. A queued event is NOT a provider acknowledgement. */ 2(function (w) { 3 'use strict'; 4 const descriptors = new Map(); 5 const secondaryQueues = new Map(); 6 const dispatched = new Set(); 7 const goals = new Set(['purchase', 'submit_lead_form', 'lr__meeting_booked__team_building', 'lr__self_purchase__team_building']); 8 const plain = value => value && typeof value === 'object' && !Array.isArray(value); 9 const baseName = name => String(name || '').replace(/^test_/, ''); 10 const allowedTag = tag => ['187081962', '187145447'].includes(String(w.LR_MICROSOFT_TAG_ID)) 11 && (String(tag) === String(w.LR_MICROSOFT_TAG_ID) 12 || (String(w.LR_MICROSOFT_TAG_ID) === '187145447' && String(tag) === '187081962')); 13 const conversionQueue = tag => { 14 tag = String(tag); 15 if (tag === String(w.LR_MICROSOFT_TAG_ID)) return (w.uetq = w.uetq || []); 16 // SCH hosts corporate funnels, but their existing conversion goals belong 17 // to LR. Keep this queue separate from SCH purchase/page-view tracking. 18 let queue = secondaryQueues.get(tag); 19 if (!queue) { 20 queue = []; 21 queue.push('consent', 'default', {ad_storage: w.LR_MICROSOFT_CONSENT === 'D' ? 'denied' : 'granted'}); 22 secondaryQueues.set(tag, queue); 23 } 24 if (Array.isArray(queue) && typeof w.UET === 'function') { 25 queue = new w.UET({ti: tag, q: queue, enableAutoSpaTracking: false}); 26 secondaryQueues.set(tag, queue); 27 } 28 return queue; 29 }; 30 w.initializeMicrosoftConversionQueues = function () { 31 for (const tag of secondaryQueues.keys()) conversionQueue(tag); 32 }; 33 const storageKey = 'lr_ms_events_' + String(w.LR_MICROSOFT_TAG_ID); 34 const readSent = () => { try { const value = JSON.parse(w.localStorage.getItem(storageKey) || '{}'); return plain(value) ? value : {}; } catch (_) { return {}; } }; 35 w.persistMicrosoftBrowserDedup = function () { 36 if (typeof w.UET !== 'function') return; 37 try { 38 const now = Math.floor(Date.now() / 1000); 39 const sent = readSent(); 40 for (const key of dispatched) sent[key] = now; 41 const entries = Object.entries(sent).filter(([, time]) => Number.isFinite(time) && time > now - 8 * 86400).slice(-100); 42 w.localStorage.setItem(storageKey, JSON.stringify(Object.fromEntries(entries))); 43 } catch (_) { /* Storage denial never interrupts conversion or checkout. */ } 44 }; 45 const allowedPid = pid => { 46 const result = {}; 47 if (plain(pid)) for (const key of ['em', 'ph']) if (/^[a-f0-9]{64}$/.test(pid[key] || '')) result[key] = pid[key]; 48 return result; 49 }; 50 const showServerDelivery = descriptor => { 51 // This receipt came from the persisted CAPI attempt, not the UET queue. 52 const receipt = descriptor.serverDelivery; 53 if (!plain(receipt) || !['accepted', 'accepted_with_warnings'].includes(receipt.status) 54 || receipt.httpStatus !== 200 || receipt.eventsReceived !== 1 55 || !Number.isInteger(receipt.attemptId) || receipt.attemptId < 1 56 || !Number.isInteger(receipt.outboxId) || receipt.outboxId < 1 57 || !Number.isInteger(receipt.attemptNumber) || receipt.attemptNumber < 1 58 || receipt.eventId !== descriptor.eventId || String(receipt.tagId) !== String(descriptor.tagId) 59 || receipt.eventName !== descriptor.eventName || receipt.value !== descriptor.value || receipt.currency !== descriptor.currency) return; 60 const render = () => { 61 try { 62 const existing = Array.from(w.document.querySelectorAll('.microsoft-server-delivery')) 63 .find(element => element.dataset.microsoftServerEvent === descriptor.eventId && element.dataset.tagId === String(descriptor.tagId)); 64 const marker = existing || w.document.createElement('div'); 65 marker.hidden = true; 66 marker.className = 'microsoft-server-delivery'; 67 Object.assign(marker.dataset, {microsoftServerEvent: receipt.eventId, tagId: String(receipt.tagId), 68 deliveryStatus: receipt.status, eventsReceived: '1', httpStatus: '200', 69 attemptId: String(receipt.attemptId), outboxId: String(receipt.outboxId), attemptNumber: String(receipt.attemptNumber), 70 eventName: receipt.eventName, value: String(receipt.value), currency: receipt.currency}); 71 if (!existing) w.document.body.appendChild(marker); 72 } catch (_) { /* Optional receipt rendering cannot break a business response. */ } 73 }; 74 if (w.document.body) render(); 75 else w.document.addEventListener('DOMContentLoaded', render, {once: true}); 76 }; 77 w.registerMicrosoftConversion = function (descriptor) { 78 if (!plain(descriptor) || !allowedTag(descriptor.tagId) 79 || !goals.has(baseName(descriptor.eventName)) || typeof descriptor.eventId !== 'string' || !descriptor.eventId 80 || !Number.isFinite(descriptor.value) || descriptor.value < 0 || !/^[A-Z]{3}$/.test(descriptor.currency)) return false;
81 descriptors.set(baseName(descriptor.eventName) + '|' + descriptor.eventId, descriptor); 82 return true; 83 }; 84 w.sendMicrosoftConversion = function (descriptor) { 85 try { 86 if (!w.registerMicrosoftConversion(descriptor)) return {status: 'skipped', reason: 'invalid_descriptor'}; 87 showServerDelivery(descriptor); 88 if (descriptor.consent === 'D' || w.LR_MICROSOFT_CONSENT === 'D') return {status: 'skipped', reason: 'consent_denied'}; 89 const now = Math.floor(Date.now() / 1000); 90 if (!Number.isInteger(descriptor.eventTime) || descriptor.eventTime < now - 7 * 86400 || descriptor.eventTime > now + 60) return {status:'skipped', reason:'invalid_event_time'}; 91 const name = (w.isTest ? 'test_' : '') + baseName(descriptor.eventName); 92 const eventName = descriptor.eventName.startsWith('test_') ? descriptor.eventName : name; 93 const key = String(descriptor.tagId) + '|' + eventName + '|' + descriptor.eventId; 94 if (dispatched.has(key) || (readSent()[key] || 0) > now - 8 * 86400) return {status: 'duplicate'}; 95 const parameters = {event_id: descriptor.eventId, transaction_id: descriptor.eventId, 96 revenue_value: descriptor.value, currency: descriptor.currency}; 97 const queue = conversionQueue(descriptor.tagId); 98 if (typeof queue.push !== 'function') return {status: 'skipped', reason: 'uet_unavailable'}; 99 const pid = allowedPid(descriptor.pid); 100 if (Object.keys(pid).length) queue.push('set', {pid}); 101 queue.push('event', eventName, parameters); 102 dispatched.add(key); 103 w.persistMicrosoftBrowserDedup(); 104 // Preserve the existing lead-value diagnostic contract. This records only 105 // the actual UET queue operation, never provider delivery or attribution. 106 if (baseName(eventName) === 'submit_lead_form') { 107 try { 108 const marker = w.document.getElementById('microsoft-tracking-done') || w.document.createElement('div'); 109 marker.id = 'microsoft-tracking-done'; 110 marker.hidden = true; 111 marker.textContent = 'V' + descriptor.value; 112 marker.dataset.deliveryStatus = 'queued'; 113 marker.dataset.eventId = descriptor.eventId; 114 if (!marker.parentNode) w.document.body.appendChild(marker); 115 } catch (_) { /* Optional diagnostics must not affect checkout or dispatch. */ } 116 } 117 if (typeof w.fetchWithParams === 'function') { 118 const auditPayload = { 119 analytics_type: 'microsoft_browser', event_type: eventName, event_value: descriptor.value, 120 currency: descriptor.currency, tag_id: descriptor.tagId, is_test: eventName.startsWith('test_') ? 1 : 0, 121 parameters: {...parameters, delivery_status: 'queued'}, 122 server_response: {status: 'queued', transport: 'uet_queue', provider_acknowledged: false} 123 }; 124 if (descriptor.businessKind === 'purchase') auditPayload.sch_tid = descriptor.businessId; 125 else if (['lead', 'meeting', 'sale'].includes(descriptor.businessKind)) auditPayload.lead_uuid = descriptor.businessId; 126 Promise.resolve(w.fetchWithParams('/api/api_v1/tracking_key_event_log', auditPayload)).then(response => { 127 if (!plain(response) || response.success !== true || !Number.isInteger(response.log_id) || response.log_id < 1) return; 128 const render = () => { 129 try { 130 const id = 'microsoft-browser-log-' + response.log_id; 131 if (w.document.getElementById(id)) return; 132 const marker = w.document.createElement('div'); 133 marker.id = id; 134 marker.hidden = true; 135 marker.className = 'microsoft-browser-log'; 136 Object.assign(marker.dataset, {eventId: descriptor.eventId, eventName, tagId: String(descriptor.tagId), 137 logId: String(response.log_id), deliveryStatus: 'queued', providerAcknowledged: 'false', 138 serverResponseLogged: 'true'}); 139 w.document.body.appendChild(marker); 140 } catch (_) { /* Audit proof rendering cannot affect dispatch. */ } 141 }; 142 if (w.document.body) render(); 143 else w.document.addEventListener('DOMContentLoaded', render, {once: true}); 144 }).catch(() => {}); 145 } 146 return {status: 'queued', eventId: descriptor.eventId}; 147 } catch (error) { return {status: 'skipped', reason: 'dispatch_failed'}; } 148 }; 149 w.convertGTAGToUET = function (command, eventName, parameters) { 150 try { 151 if (command !== 'event' || typeof eventName !== 'string') return {status: 'ignored'}; 152 parameters = plain(parameters) ? parameters : {}; 153 let name = baseName(eventName); 154 if (name === 'generate_lead') name = 'submit_lead_form'; 155 if (goals.has(name)) { 156 const descriptor = descriptors.get(name + '|' + String(parameters.event_id || parameters.transaction_id || '')); 157 return descriptor ? w.sendMicrosoftConversion(descriptor) : {status: 'skipped', reason: 'missing_authoritative_descriptor'}; 158 } 159 const safe = {}; 160 if (w.LR_MICROSOFT_CONSENT === 'D') return {status:'skipped', reason:'c
160onsent_denied'}; 161 for (const key of ['event_category', 'event_label', 'event_value']) { 162 if (typeof parameters[key] === 'string' || typeof parameters[key] === 'number') safe[key] = parameters[key]; 163 } 164 if (Number.isFinite(parameters.value) && /^[A-Z]{3}$/.test(parameters.currency || '')) { 165 safe.revenue_value = parameters.value; safe.currency = parameters.currency; 166 } 167 w.uetq = w.uetq || []; 168 if (typeof w.uetq.push !== 'function') return {status: 'skipped', reason: 'uet_unavailable'}; 169 w.uetq.push('event', (w.isTest || eventName.startsWith('test_') ? 'test_' : '') + name, safe); 170 return {status: 'queued'}; 171 } catch (error) { return {status: 'skipped', reason: 'invalid_event'}; } 172 }; 173})(window);
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.