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