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