1( function () { 2 document.addEventListener( 'DOMContentLoaded', function () { 3 4 /** 5 * This JS function is responsible for tracking form submissions from tee time booking iframes 6 * to push events to Google Tag Manager via dataLayer. It listens for postMessage events from iframes. 7 * 8 * It covers 2 cases: 9 * 1) iframe is already present on a page, 10 * 2) iframe is added by a JS script, for this case Mutation Observer is needed 11 */ 12 const FormSubmitTracker = { 13 // Configuration 14 iframeUrlParts: [ 15 ".svc.golf", 16 ".book.teeitup." 17 ], 18 19 // The event name to listen for in postMessage from iframe 20 EVENT_NAME: "form_submitted", 21 22 // State 23 observer: null, 24 isListening: false, 25 26 // Helpers 27 getIframes() { 28 return Array.from(document.querySelectorAll("iframe")); 29 }, 30 31 iframeMatches(iframe) { 32 if (!iframe || !iframe.src) return false; 33 34 return this.iframeUrlParts.some((part) => 35 iframe.src.includes(part) 36 ); 37 }, 38 39 findTargetIframe() { 40 return this.getIframes().find((iframe) => 41 this.iframeMatches(iframe) 42 ); 43 }, 44 45 // Message handler 46 onMessage(event) { 47 const isAllowedOrigin = this.iframeUrlParts.some((part) => 48 event.origin.includes(part) 49 ); 50 51 if (!isAllowedOrigin) return; 52 53 if (!event.data || event.data.event !== this.EVENT_NAME) return; 54 55 window.dataLayer = Array.isArray(window.dataLayer) 56 ? window.dataLayer 57 : []; 58 window.dataLayer.push({ 59 event: this.EVENT_NAME, 60 formType: event.data.value 61 }); 62 }, 63 64 // Attach listener once 65 bindListener() { 66 if (this.isListening) return; 67 68 window.addEventListener("message", (e) => this.onMessage(e)); 69 this.isListening = true; 70 }, 71 72 // Stop observing DOM 73 stopWatching() { 74 if (!this.observer) return; 75 76 this.observer.disconnect(); 77 this.observer = null; 78 }, 79 80 // Core check logic 81 check() { 82 const targetIframe = this.findTargetIframe(); 83 84 if (targetIframe) { 85 this.bindListener(); 86 this.stopWatching(); 87 } 88 }, 89 90 // Observe DOM for dynamically added iframes 91 watch() { 92 if (this.observer) return; 93 94 this.observer = new MutationObserver(() => this.check()); 95 96 this.observer.observe(document.documentElement, { 97 childList: true, 98 subtree: true 99 }); 100 }, 101 102 // Entry point 103 init(options = {}) { 104 // Allow override of iframeUrlParts 105 if (options.iframeUrlParts) { 106 this.iframeUrlParts = Array.isArray(options.iframeUrlParts) 107 ? options.iframeUrlParts 108 : [options.iframeUrlParts]; 109 } 110 111 // Initial check 112 this.check(); 113 114 // Start observing if not yet matched 115 if (!this.isListening) { 116 this.watch(); 117 } 118 } 119 }; 120 121 // Init 122 FormSubmitTracker.init({ 123 iframeUrlParts: [ 124 ".svc.golf", 125 ".book.teeitup.golf" 126 ] 127 }); 128 129 }); 130} )();
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.