1// This file will be run in the footer on every page. 2// It should be used for content that relates 3// to your site as a whole, it should not be used for 4// plugin-specific js 5 6require(["jquery", "site_gtm", "plugins_common_custom_ui_watcher", "sv_load!plugins_dynamic_content", "domReady!"], function($, gtm, UIWatcher, dynamic) { 7 gtm.update(); 8 9 // SEM tracking 10 var uiEvents = new UIWatcher({}, $, document); 11 uiEvents.on("change-page", handlePageChange); 12 uiEvents.on("view-change", handleFilterChange); 13 uiEvents.on("sort-change", handleFilterChange); 14 uiEvents.on("value-change", handleFilterChange); 15 uiEvents.on("toggle-show-more", handleShowMoreToggle); 16 uiEvents.on("scroll-into-view", handleScrollIntoView); 17 uiEvents.on("item-click", handleItemClick); 18 uiEvents.on("map-pin-click", handleMapPinClick); 19 uiEvents.on("tripbuilder", handleTripbuilder); 20 uiEvents.on("qv-tripbuilder", handleTripbuilder); 21 uiEvents.on("quick-view", handleQuickview); 22 23 // tracking handlers 24 function handleTripbuilder(payload){ 25 var event = payload.eventContext, 26 widget = payload.widgetContext; 27 let ga4Data = { 28 eventName: event?.value === "Add" ? "add_to_favorites" : undefined, 29 itemType: event?.itemType ?? (widget?.name === "Coupons" ? 'offers' : !["quickview", "quick view"].includes(widget?.name.toLowerCase()) ? widget?.name.toLowerCase() : undefined), 30 itemName: event?.refTitle, 31 linkObject: event?.linkObject, 32 itemCategory: event?.crmc, 33 interactionSource: event?.interactionSource, 34 interactionText: event?.interactionText, 35 interactionUrl: event?.interactionUrl, 36 itemCity: event?.itemCity, 37 itemRegion: event?.itemRegion, 38 itemId: event?.itemId, 39 acctId: event?.acctId, 40 itemVariant: event?.itemVariant, 41 price: event?.price, 42 } 43 if (event?.crmsc) 44 ga4Data.itemSubcategory = event?.crmsc; 45 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 46 gtm.sendEvents({ tClient_ga4: ga4Data }); 47 } 48 49 function handleQuickview(payload){ 50 var event = payload.eventContext; 51 let ga4Data = { 52 eventName: event?.eventName, 53 itemType: event?.itemType, 54 itemName: event?.itemName ?? event?.refTitle, 55 linkObject: event?.linkObject, 56 itemCategory: event?.crmc, 57 interactionSource: event?.interactionSource, 58 interactionText: event?.interactionText, 59 interactionUrl: event?.interactionUrl, 60 itemCity: event?.itemCity, 61 itemRegion: event?.itemRegion, 62 itemId: event?.itemId, 63 acctId: event?.acctId, 64 itemVariant: event?.itemVariant, 65 price: event?.price, 66 referralType: event?.referralType, 67 } 68 if (event?.crmsc) 69 ga4Data.itemSubcategory = event?.crmsc; 70 if (event?.eventName === 'partner_referral') 71 ga4Data.value = 5; 72 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 73 gtm.sendEvents({ tClient_ga4: ga4Data }); 74 } 75 76 function handleScrollIntoView(payload){ 77 var event = payload.eventContext, 78 widget = payload.widgetContext; 79 var data = { 80 eventCategory: widget.name, 81 eventAction: event.name, 82 eventLabel: "Widget - "+widget.name, 83 nonInteraction: true 84 } 85 if (data.eventCategory && data.eventAction && data.eventLabel) 86 gtm.sendEvents({tClient: data}); 87 } 88 89 function handleShowMoreToggle(payload){ 90 var event = payload.eventContext; 91 let ga4Data = { 92 eventName: "filter", 93 filterType: `${event?.refTitle} | ${event?.displayText}`, 94 itemType: event?.itemType, 95 interactionSource: event?.interactionSource, 96 interactionText: event?.interactionText, 97 } 98 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 99 gtm.sendEvents({ tClient_ga4: ga4Data }); 100 } 101 102 function handlePageChange(payload){ 103 var event = payload.eventContext; 104 let ga4Data = { 105 eventName: "filter", 106 filterType: `${event?.refTitle} | ${event?.displayText}`, 107 itemType: event?.itemType, 108 interactionSource: event?.interactionSource, 109 interactionText: event?.interactionText, 110 } 111 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 112 gtm.sendEvents({ tClient_ga4: ga4Data }); 113 } 114
115 function handleItemClick(payload){ 116 var event = payload.eventContext; 117 if (event?.name === "Quickview") { 118 var ga4Data = { 119 eventName: "filter", 120 filterType: "Open Quickview", 121 itemType: event?.itemType, 122 itemName: event?.itemName ?? event?.refTitle, 123 interactionSource: event?.interactionSource, 124 interactionText: event?.interactionText, 125 itemCategory: event?.crmc, 126 itemCity: event?.itemCity, 127 itemRegion: event?.itemRegion, 128 itemId: event?.itemId, 129 acctId: event?.acctId, 130 itemVariant: event?.itemVariant, 131 price: event?.price, 132 } 133 if (event?.crmsc) 134 ga4Data.itemSubcategory = event?.crmsc; 135 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 136 gtm.sendEvents({tClient_ga4: ga4Data}); 137 } 138 else { 139 var ga4Data = { 140 eventName: event?.eventName, 141 itemType: event?.itemType, 142 itemName: event?.itemName ?? event?.refTitle, 143 linkObject: event?.linkObject, 144 itemCategory: event?.crmc, 145 interactionSource: event?.interactionSource, 146 interactionText: event?.interactionText, 147 interactionUrl: event?.interactionUrl, 148 itemCity: event?.itemCity, 149 itemRegion: event?.itemRegion, 150 itemId: event?.itemId, 151 acctId: event?.acctId, 152 itemVariant: event?.itemVariant, 153 price: event?.price, 154 referralType: event?.referralType, 155 } 156 if (event?.crmsc) 157 ga4Data.itemSubcategory = event?.crmsc; 158 if (event?.eventName === 'partner_referral') 159 ga4Data.value = 5; 160 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 161 gtm.sendEvents({tClient_ga4: ga4Data}); 162 } 163 } 164 165 function handleMapPinClick(payload){ 166 var event = payload.eventContext, 167 widget = payload.widgetContext; 168 var data = { 169 eventCategory: widget.name, 170 eventAction: event.name, 171 eventLabel: event.refTitle, 172 ot: event.type, 173 nonInteraction: false 174 } 175 if (data.eventCategory && data.eventAction && data.eventLabel) 176 gtm.sendEvents({tClient: data}); 177 } 178 179 function handleFilterChange (payload) { 180 var event = payload.eventContext; 181 let ga4Data = { 182 eventName: "filter", 183 filterType: `${event?.refTitle} | ${event?.displayText}`, 184 itemType: event?.itemType, 185 interactionSource: event?.interactionSource, 186 interactionText: event?.interactionText, 187 } 188 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 189 gtm.sendEvents({ tClient_ga4: ga4Data }); 190 } 191 // end SEM tracking 192}); 193 194// Lazy-load js for widgets that have the [data-js] attribute on their root node 195(function() { 196 function initDataJs(searchScope, selector = '[data-js]' ){ 197 require(['plugins_common_custom_lazyload'], function(lazyload) { 198 var widgetsToLoad = []; 199 let scope = searchScope ? searchScope : document; 200 var els = scope.querySelectorAll(selector); 201 202 for (var i = 0; i < els.length; i++) { 203 var jsFile = els[i].dataset.js || ''; 204 205 // Collection modules establish their layout and reveal the widget. 206 // Waiting for intersection can permanently skip a collection when 207 // earlier widgets initialise and shift it across the viewport. 208 if (jsFile.indexOf('plugins_collections_') === 0) { 209 loadWidget(els[i]); 210 } else { 211 widgetsToLoad.push(els[i]); 212 } 213 } 214 215 lazyload.lazy(widgetsToLoad, function(entry, io) { 216 loadWidget(entry.target, io); 217 }); 218 219 function loadWidget(target, io) { 220 var jsFile = target.dataset.js; 221 var guid = target.dataset.guid; 222 223 require([jsFile], function(widget) { 224 widget.init(guid); 225 226 if (io) io.unobserve(target); 227 }); 228 } 229 }); 230 }; 231 232 initDataJs(); 233 234 let eventToSelectorMap = { 235 // each event needs a slightly different selector 236 "dynamicContentLoaded": "[data-dynamic-chosen] > .contentRender > [data-js]", 237 "abContentLoaded": "[data-ab-chosen] > .contentRender > [data-js]" 238 } 239 240 Object.entries(eventToSelectorMap).forEach( event => { 241 const [eventName, selector] = event; 242 window.addEventListener(eventName, e => { 243 const collections = document.querySelector(`[data-guid="${e}"]`); 244 initDataJs(collections, selector); 245 }); 246 }); 247})(); 248 249// Remove critical styles from head after all CSS files have loaded 250// This prevents a situation where the critical styles cause an override of the CSS in files
251const criticalStyles = document.querySelector('[data-critical-styles]'); 252if (criticalStyles) { 253 window.addEventListener('load', e => criticalStyles.remove()); 254}
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.