1(() => { 2 // ===================== CONFIG ===================== 3 // Map RTM formId (from e.detail.payload.formId) -> Sitecore Goal GUID 4 //const GOAL_MAP = { 5 // "0b966e03-2b63-f011-bec1-000d3a4f14e7": "{D5231E05-2184-41F0-B83C-FE82EE16AE61}", 6 //}; 7 8 // Your handler endpoint (no trailing .ashx needed if youâve mapped it) 9 const ENDPOINT = "/sitecore/analytics/ClientEventTracker.ashx"; 10 11 // Optional: gate by consent 12 const hasAnalyticsConsent = () => true; // plug into your CMP if needed 13 14 // Toggle debug logging 15 const DEBUG = false; 16 // ================================================== 17 18 const log = (...a) => DEBUG && console.log("[RTMâxDB]", ...a); 19 20 // Prevent double-firing (same form instance in this pageview) 21 const firedForms = new Set(); 22 23 function getGoalIdFor(formId, formEl) { 24 // 1) mapping table (preferred) 25 //if (formId && GOAL_MAP[formId]) return GOAL_MAP[formId]; 26 27 // 2) fallback: DOM attribute someone may have added 28 const attr = formEl?.getAttribute?.("data-goal-id"); 29 if (attr) return attr; 30 31 return null; 32 } 33 34 function getPageId() { 35 const itemId = document.querySelector('meta[name="itemId"]')?.content || ''; 36 return itemId; 37 38 } 39 40 function sendToTracker({ goalId, formId, pageId, eventType = "RTM_SUBMIT", pageUrl = location.href }) { 41 if (!goalId) return; 42 43 const url = `${ENDPOINT}?EventId=${encodeURIComponent(goalId)}&PageId=${encodeURIComponent(pageId)}&EventType=${encodeURIComponent(eventType)}&FormId=${encodeURIComponent(formId || "")}&PageUrl=${encodeURIComponent(pageUrl)}`; 44 // Fire-and-forget GET 45 try { 46 // Prefer fetch with keepalive; fall back to Image ping. 47 if ("fetch" in window) { 48 fetch(url, { method: "GET", keepalive: true }).catch(() => { }); 49 } else { 50 const img = new Image(); 51 img.src = url + "&cb=" + Date.now(); 52 } 53 } catch { } 54 return; 55 56 } 57 58 function handleAfterSubmit(e) { 59 if (!hasAnalyticsConsent()) return; 60 61 const detail = e?.detail || {}; 62 if (detail.success === false) { 63 log("submit not successful; skipping"); 64 return; 65 } 66 67 const formId = document.querySelector("[data-form-id]")?.getAttribute?.("data-form-id"); 68 const formEl = document.querySelector("form.marketingForm"); 69 70 if (!formId) { 71 log("no formId in event payload; skipping", e); 72 return; 73 } 74 if (firedForms.has(formId)) { 75 log("already fired for formId", formId); 76 return; 77 } 78 79 const pageId = getPageId(); 80 81 const goalId = getGoalIdFor(formId, formEl); 82 if (!goalId) { 83 log("no goal mapping/attribute for formId", formId); 84 return; 85 } 86 87 firedForms.add(formId); 88 log("sending goal", { goalId, formId }); 89 sendToTracker({ goalId, formId, pageId, eventType: formId }); 90 } 91 92 function handleAfterLoad(e) { 93 // Helpful for local testing: log/record formId as forms appear 94 const formId = e?.detail?.payload?.formId || e.target?.getAttribute?.("data-form-id") || ""; 95 if (formId) log("form loaded", formId); 96 } 97 98 // Listen globally (RTM injects forms after initial load) 99 document.addEventListener("d365mkt-afterformsubmit", handleAfterSubmit); 100 document.addEventListener("d365mkt-afterformload", handleAfterLoad); 101 102})();
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.