1/** 2 * Availability Search Engine Request Interceptor (early-load) 3 * 4 * This script MUST load before the Booking Engine API client initialises. 5 * It patches both fetch and XMLHttpRequest to capture availability search 6 * requests and buffer them for the GA event tracking registry to 7 * process once it loads. 8 * 9 * The host to intercept is read from the <meta name="availabilitySearchUrl"> 10 * tag so it works across environments without hardcoding. 11 * 12 * Buffered responses are stored in window.__gaAvailabilityBuffer and 13 * consumed by registerAvailabilitySearchTracking() in ga-event-tracking-registry.js. 14 */ 15(function () { 16 window.__gaAvailabilityBuffer = []; 17 18 /** 19 * Resolve the availability search engine host from the meta tag. 20 * Returns the origin (e.g. "https://availability-search-engine.staging.destinationcore.io") 21 * or null if the meta tag is absent. 22 */ 23 function getAvailabilityHost() { 24 if (window.__gaAvailabilityHost !== undefined) { 25 return window.__gaAvailabilityHost; 26 } 27 28 var meta = document.querySelector('meta[name="availabilitySearchUrl"]'); 29 if (!meta || !meta.content) { 30 window.__gaAvailabilityHost = null; 31 return null; 32 } 33 34 // Normalise: ensure scheme, strip trailing slash 35 var host = meta.content.trim().replace(/\/+$/, ""); 36 if (host.indexOf("://") === -1) { 37 host = "https://" + host; 38 } 39 40 try { 41 window.__gaAvailabilityHost = new URL(host).origin; 42 } catch (e) { 43 window.__gaAvailabilityHost = null; 44 } 45 46 return window.__gaAvailabilityHost; 47 } 48 49 function isAvailabilityRequest(url) { 50 var host = getAvailabilityHost(); 51 if (!host) { 52 return false; 53 } 54 return typeof url === "string" && url.indexOf(host) !== -1; 55 } 56 57 /** 58 * Determine the request type from the URL path. 59 * - "search_create" â POST /api/v1/search/create 60 * - "search_results" â GET /api/v1/search/{uuid}/results 61 * - "booking_intent" â POST /api/v1/booking-intent/create 62 * - null â not an availability request we care about 63 */ 64 function getRequestType(url) { 65 if (!isAvailabilityRequest(url)) { 66 return null; 67 } 68 if (url.indexOf("/api/v1/search/create") !== -1) { 69 return "search_create"; 70 } 71 if (url.indexOf("/results") !== -1 && url.indexOf("/api/v1/search/") !== -1) { 72 return "search_results"; 73 } 74 if (url.indexOf("/api/v1/booking-intent/create") !== -1) { 75 return "booking_intent"; 76 } 77 return null; 78 } 79 80 // --- Patch fetch --- 81 var originalFetch = window.fetch; 82 window.fetch = function () { 83 var url = arguments[0]; 84 var opts = arguments[1]; 85 var urlStr = typeof url === "string" ? url : (url && url.url ? url.url : ""); 86 87 var requestType = getRequestType(urlStr); 88 if (!requestType) { 89 return originalFetch.apply(this, arguments); 90 } 91 92 var fetchBody = opts && opts.body ? opts.body : null; 93 94 return originalFetch.apply(this, arguments).then(function (response) { 95 var clone = response.clone(); 96 clone.json().then(function (data) { 97 window.__gaAvailabilityBuffer.push({ 98 data: data, 99 body: fetchBody, 100 type: requestType, 101 url: urlStr, 102 status: response.status, 103 }); 104 }).catch(function () {}); 105 return response; 106 }); 107 }; 108 109 // --- Patch XMLHttpRequest --- 110 var originalXHROpen = XMLHttpRequest.prototype.open; 111 var originalXHRSend = XMLHttpRequest.prototype.send; 112 113 XMLHttpRequest.prototype.open = function () { 114 this._gaAvailUrl = arguments[1] || ""; 115 this._gaAvailMethod = arguments[0] || ""; 116 return originalXHROpen.apply(this, arguments); 117 }; 118 119 XMLHttpRequest.prototype.send = function (body) { 120 var xhr = this; 121 var xhrBody = body; 122 123 var requestType = getRequestType(xhr._gaAvailUrl || ""); 124 if (requestType) { 125 xhr.addEventListener("load", function () { 126 try { 127 var data = JSON.parse(xhr.responseText); 128 window.__gaAvailabilityBuffer.push({ 129 data: data, 130 body: xhrBody, 131 type: requestType, 132 url: xhr._gaAvailUrl, 133 status: xhr.status, 134 }); 135 } catch (e) { 136 // Silently ignore parse errors 137 } 138 }); 139 } 140 141 return originalXHRSend.apply(this, arguments); 142 }; 143})();
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.