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("quick-view", handleQuickview); 21 22 // tracking handlers 23 function handleTripbuilder(payload) { 24 var event = payload.eventContext, 25 widget = payload.widgetContext; 26 let ga4Data = { 27 eventName: event?.value === "Add" ? "add_to_favorites" : undefined, 28 itemType: event?.itemType ?? (widget?.name === "Coupons" ? 'offers' : !["quickview", "quick view"].includes(widget?.name.toLowerCase()) ? widget?.name.toLowerCase() : undefined), 29 itemName: event.itemName ?? event.refTitle, 30 linkObject: event?.linkObject, 31 itemCategory: event?.crmc, 32 interactionSource: event?.interactionSource, 33 interactionText: event?.interactionText, 34 interactionUrl: event?.interactionUrl, 35 itemCity: event?.itemCity, 36 itemRegion: event?.itemRegion, 37 itemId: event?.itemId, 38 acctId: event?.acctId, 39 itemVariant: event?.itemVariant, 40 price: event?.price, 41 } 42 if (event?.crmsc) 43 ga4Data.itemSubcategory = event?.crmsc; 44 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 45 gtm.sendEvents({ tClient_ga4: ga4Data }); 46 } 47 48 function handleQuickview(payload) { 49 var event = payload.eventContext; 50 let ga4Data = { 51 eventName: event?.eventName, 52 itemType: event?.itemType, 53 itemName: event?.itemName ?? event?.refTitle, 54 linkObject: event?.linkObject, 55 itemCategory: event?.crmc, 56 interactionSource: event?.interactionSource, 57 interactionText: event?.interactionText, 58 interactionUrl: event?.interactionUrl, 59 itemCity: event?.itemCity, 60 itemRegion: event?.itemRegion, 61 itemId: event?.itemId, 62 acctId: event?.acctId, 63 itemVariant: event?.itemVariant, 64 price: event?.price, 65 referralType: event?.referralType, 66 } 67 if (event?.crmsc) 68 ga4Data.itemSubcategory = event?.crmsc; 69 if (event?.eventName === 'partner_referral') 70 ga4Data.value = 5; 71 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 72 gtm.sendEvents({ tClient_ga4: ga4Data }); 73 } 74 75 function handleScrollIntoView(payload) { 76 var event = payload.eventContext, 77 widget = payload.widgetContext; 78 } 79 80 function handleShowMoreToggle(payload) { 81 var event = payload.eventContext; 82 let ga4Data = { 83 eventName: "filter", 84 filterType: `${event?.refTitle} | ${event?.displayText}`, 85 itemType: event?.itemType, 86 interactionSource: event?.interactionSource, 87 interactionText: event?.interactionText, 88 } 89 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 90 gtm.sendEvents({ tClient_ga4: ga4Data }); 91 } 92 93 function handlePageChange(payload) { 94 var event = payload.eventContext; 95 let ga4Data = { 96 eventName: "filter", 97 filterType: `${event?.refTitle} | ${event?.displayText}`, 98 itemType: event?.itemType, 99 interactionSource: event?.interactionSource, 100 interactionText: event?.interactionText, 101 } 102 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 103 gtm.sendEvents({ tClient_ga4: ga4Data }); 104 } 105 106 function handleItemClick(payload) { 107 var event = payload.eventContext; 108 if (event?.name === "Quickview" || event?.name === "Quick View") { 109 var ga4Data = { 110 eventName: "filter", 111 filterType: "Open Quickview", 112 itemType: event?.itemType, 113 itemName: event?.itemName ?? event?.refTitle, 114 interactionSource: event?.interactionSource, 115 interactionText: event?.interactionText, 116 itemCategory: event?.crmc, 117 itemCity: event?.itemCity, 118 itemRegion: event?.itemRegion, 119 itemId: event?.itemId, 120 acctId: event?.acctId, 121 itemVariant: event?.itemVariant, 122 price: event?.price, 123 } 124 if (event?.crmsc) 125 ga4Data.itemSubcategory = event?.crmsc; 126 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 127 gtm.sendEvents({tClient_ga4: ga4Data}); 128 } else { 129 var ga4Data = { 130 eventName: event?.eventName, 131 itemType: event?.itemType, 132 itemName: event?.itemName ?? event?.refTitle, 133 linkObject: event?.linkObject, 134 itemCategory: event?.crmc, 135 interactionSource: event?.interactionSource, 136 interactionText: event?.interactionText, 137 interactionUrl: event?.interactionUrl, 138 itemCity: event?.itemCity, 139 itemRegion: event?.itemRegion, 140 itemId: event?.itemId, 141 acctId: event?.acctId, 142 itemVariant: event?.itemVariant, 143 price: event?.price, 144 referralType: event?.referralType, 145 } 146 if (event?.crmsc) 147 ga4Data.itemSubcategory = event?.crmsc; 148 if (event?.eventName === 'partner_referral') 149 ga4Data.value = 5; 150 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 151 gtm.sendEvents({tClient_ga4: ga4Data}); 152 } 153 } 154 155 function handleMapPinClick(payload) { 156 var event = payload.eventContext, 157 widget = payload.widgetContext; 158 } 159 160 function handleFilterChange(payload) { 161 var event = payload.eventContext; 162 let ga4Data = { 163 eventName: "filter", 164 filterType: `${event?.refTitle} | ${event?.displayText}`, 165 itemType: event?.itemType, 166 interactionSource: event?.interactionSource, 167 interactionText: event?.interactionText, 168 } 169 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 170 gtm.sendEvents({ tClient_ga4: ga4Data }); 171 } 172 // end SEM tracking 173}); 174 175// Lazy-load FontAwesome 176(function () { 177 var css = document.createElement('link'); 178 css.href = 'https://use.fontawesome.com/releases/v5.14.0/css/all.css'; 179 css.rel = 'stylesheet'; 180 css.type = 'text/css'; 181 document.getElementsByTagName('head')[0].appendChild(css); 182})(); 183 184// Lazy-load js for widgets that have the [data-js] attribute on their root node 185(function () { 186 function initDataJs(searchScope, selector = '[data-js]') { 187 require(['plugins_common_custom_lazyload'], function (lazyload) { 188 var widgetsToLoad = []; 189 let scope = searchScope ? searchScope : document; 190 var els = scope.querySelectorAll(selector); 191 192 for (var i = 0; i < els.length; i++) { 193 widgetsToLoad.push(els[i]); 194 } 195 196 lazyload.lazy(widgetsToLoad, function (entry, io) { 197 var target = entry.target; 198 var jsFile = target.dataset.js; 199 var guid = target.dataset.guid; 200 201 require([jsFile], function (widget) { 202 widget.init(guid); 203 io.unobserve(target); 204 }); 205 }); 206 }); 207 }; 208 209 initDataJs(); 210 211 let eventToSelectorMap = { 212 // each event needs a slightly different selector 213 "dynamicContentLoaded": "[data-dynamic-chosen] [data-js]", 214 "abContentLoaded": "[data-ab-chosen] [data-js]" 215 } 216
217 Object.entries(eventToSelectorMap).forEach( event => { 218 const [eventName, selector] = event; 219 window.addEventListener(eventName, e => { 220 const collections = document.querySelector(`[data-guid="${e}"]`); 221 initDataJs(collections, selector); 222 }); 223 }); 224})(); 225 226// Remove critical styles from head after all CSS files have loaded 227// This prevents a situation where the critical styles cause an override of the CSS in files 228const criticalStyles = document.querySelector('[data-critical-styles]'); 229if (criticalStyles) { 230 window.addEventListener('load', e => criticalStyles.remove()); 231}
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.