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.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 } 80 81 function handleShowMoreToggle(payload){ 82 var event = payload.eventContext; 83 let ga4Data = { 84 eventName: "filter", 85 filterType: `${event?.refTitle} | ${event?.displayText}`, 86 itemType: event?.itemType, 87 interactionSource: event?.interactionSource, 88 interactionText: event?.interactionText, 89 } 90 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 91 gtm.sendEvents({ tClient_ga4: ga4Data }); 92 } 93 94 function handlePageChange(payload){ 95 var event = payload.eventContext; 96 let ga4Data = { 97 eventName: "filter", 98 filterType: `${event?.refTitle} | ${event?.displayText}`, 99 itemType: event?.itemType, 100 interactionSource: event?.interactionSource, 101 interactionText: event?.interactionText, 102 } 103 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 104 gtm.sendEvents({ tClient_ga4: ga4Data }); 105 } 106 107 function handleItemClick(payload){ 108 var event = payload.eventContext; 109 if (event?.name === "Quickview" || event?.name === "Quick View") { 110 var ga4Data = { 111 eventName: "filter", 112 filterType: "Open Quickview", 113 itemType: event?.itemType, 114 itemName: event?.itemName ?? event?.refTitle, 115 interactionSource: event?.interactionSource, 116 interactionText: event?.interactionText, 117 itemCategory: event?.crmc, 118 itemCity: event?.itemCity, 119 itemRegion: event?.itemRegion, 120 itemId: event?.itemId, 121 acctId: event?.acctId, 122 itemVariant: event?.itemVariant, 123 price: event?.price, 124 } 125 if (event?.crmsc) 126 ga4Data.itemSubcategory = event?.crmsc; 127 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 128 gtm.sendEvents({tClient_ga4: ga4Data}); 129 } else { 130 var ga4Data = { 131 eventName: event?.eventName, 132 itemType: event?.itemType, 133 itemName: event?.itemName ?? event?.refTitle, 134 linkObject: event?.linkObject, 135 itemCategory: event?.crmc, 136 interactionSource: event?.interactionSource, 137 interactionText: event?.interactionText, 138 interactionUrl: event?.interactionUrl, 139 itemCity: event?.itemCity, 140 itemRegion: event?.itemRegion, 141 itemId: event?.itemId, 142 acctId: event?.acctId, 143 itemVariant: event?.itemVariant, 144 price: event?.price, 145 referralType: event?.referralType, 146 } 147 if (event?.crmsc) 148 ga4Data.itemSubcategory = event?.crmsc; 149 if (event?.eventName === 'partner_referral') 150 ga4Data.value = 5; 151 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 152 gtm.sendEvents({tClient_ga4: ga4Data}); 153 } 154 } 155 156 function handleMapPinClick(payload){ 157 var event = payload.eventContext, 158 widget = payload.widgetContext; 159 } 160
161 function handleFilterChange (payload) { 162 var event = payload.eventContext; 163 let ga4Data = { 164 eventName: "filter", 165 filterType: `${event?.refTitle} | ${event?.displayText}`, 166 itemType: event?.itemType, 167 interactionSource: event?.interactionSource, 168 interactionText: event?.interactionText, 169 } 170 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 171 gtm.sendEvents({ tClient_ga4: ga4Data }); 172 } 173 // end SEM tracking 174}); 175 176// Lazy-load js for widgets that have the [data-js] attribute on their root node 177(function() { 178 function initDataJs(searchScope, selector = '[data-js]' ){ 179 require(['plugins_common_custom_lazyload'], function(lazyload) { 180 var widgetsToLoad = []; 181 let scope = searchScope ? searchScope : document; 182 var els = scope.querySelectorAll(selector); 183 184 for (var i = 0; i < els.length; i++) { 185 widgetsToLoad.push(els[i]); 186 } 187 188 lazyload.lazy(widgetsToLoad, function(entry, io) { 189 var target = entry.target; 190 var jsFile = target.dataset.js; 191 var guid = target.dataset.guid; 192 193 require([jsFile], function(widget) { 194 widget.init(guid); 195 io.unobserve(target); 196 }); 197 }); 198 }); 199 }; 200 201 initDataJs(); 202 203 let eventToSelectorMap = { 204 // each event needs a slightly different selector 205 "dynamicContentLoaded": "[data-dynamic-chosen] [data-js]", 206 "abContentLoaded": "[data-ab-chosen] [data-js]" 207 } 208 209 Object.entries(eventToSelectorMap).forEach( event => { 210 const [eventName, selector] = event; 211 window.addEventListener(eventName, e => { 212 const collections = document.querySelector(`[data-guid="${e}"]`); 213 initDataJs(collections, selector); 214 }); 215 }); 216})(); 217 218// Wrap core_textbox iframes to prevent horizontal overflow 219(function() { 220 const textBoxes = document.querySelectorAll('[data-sv-core-textbox]'); 221 if (textBoxes.length) { 222 require(['plugins_common_custom_utils'], function(utils) { 223 textBoxes.forEach(utils.wrapIframes); 224 }); 225 } 226})(); 227 228// Init quickview textboxes 229(function() { 230 const textBoxes = document.querySelectorAll('[data-qv-enabled-textbox]'); 231 if (textBoxes.length) { 232 require(['plugins_common_custom_quickview_textbox'], function (qv) { 233 qv.init(); 234 }); 235 } 236})(); 237 238// Remove critical styles from head after all CSS files have loaded 239// This prevents a situation where the critical styles cause an override of the CSS in files 240const criticalStyles = document.querySelector('[data-critical-styles]'); 241if (criticalStyles) { 242 window.addEventListener('load', e =>
242 criticalStyles.remove()); 243}
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.