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("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 } 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 referralType: event?.referralType, 63 } 64 if (event?.crmsc) 65 ga4Data.itemSubcategory = event?.crmsc; 66 if (event?.eventName === 'partner_referral') 67 ga4Data.value = 5; 68 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 69 gtm.sendEvents({ tClient_ga4: ga4Data }); 70 } 71 72 function handleScrollIntoView(payload){ 73 var event = payload.eventContext, 74 widget = payload.widgetContext; 75 } 76 77 function handleShowMoreToggle(payload){ 78 var event = payload.eventContext; 79 let ga4Data = { 80 eventName: "filter", 81 filterType: `${event?.refTitle} | ${event?.displayText}`, 82 itemType: event?.itemType, 83 interactionSource: event?.interactionSource, 84 interactionText: event?.interactionText, 85 } 86 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 87 gtm.sendEvents({ tClient_ga4: ga4Data }); 88 } 89 90 function handlePageChange(payload){ 91 var event = payload.eventContext; 92 let ga4Data = { 93 eventName: "filter", 94 filterType: `${event?.refTitle} | ${event?.displayText}`, 95 itemType: event?.itemType, 96 interactionSource: event?.interactionSource, 97 interactionText: event?.interactionText, 98 } 99 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 100 gtm.sendEvents({ tClient_ga4: ga4Data }); 101 } 102 103 function handleItemClick(payload){ 104 var event = payload.eventContext; 105 if (event?.name === "Quickview") { 106 var ga4Data = { 107 eventName: "filter", 108 filterType: "Open Quickview", 109 itemType: event?.itemType, 110 itemName: event?.itemName ?? event?.refTitle, 111 interactionSource: event?.interactionSource, 112 interactionText: event?.interactionText, 113 itemCategory: event?.crmc, 114 itemCity: event?.itemCity, 115 itemRegion: event?.itemRegion, 116 itemId: event?.itemId, 117 acctId: event?.acctId, 118 } 119 if (event?.crmsc) 120 ga4Data.itemSubcategory = event?.crmsc; 121 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 122 gtm.sendEvents({tClient_ga4: ga4Data}); 123 } 124 else { 125 var ga4Data = { 126 eventName: event?.eventName, 127 itemType: event?.itemType, 128 itemName: event?.itemName ?? event?.refTitle, 129 linkObject: event?.linkObject, 130 itemCategory: event?.crmc, 131 interactionSource: event?.interactionSource, 132 interactionText: event?.interactionText, 133 interactionUrl: event?.interactionUrl, 134 itemCity: event?.itemCity, 135 itemRegion: event?.itemRegion, 136 itemId: event?.itemId, 137 acctId: event?.acctId, 138 referralType: event?.referralType, 139 } 140 if (event?.crmsc) 141 ga4Data.itemSubcategory = event?.crmsc; 142 if (event?.eventName === 'partner_referral') 143 ga4Data.value = 5; 144 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 145 gtm.sendEvents({tClient_ga4: ga4Data}); 146 } 147 } 148 149 function handleMapPinClick(payload){ 150 var event = payload.eventContext, 151 widget = payload.widgetContext; 152 } 153 154 function handleFilterChange (payload) { 155 var event = payload.eventContext; 156 let ga4Data = { 157 eventName: "filter", 158 filterType: `${event?.refTitle} | ${event?.displayText}`, 159 itemType: event?.itemType, 160 interactionSource: event?.interactionSource, 161 interactionText: event?.interactionText, 162 } 163 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 164 gtm.sendEvents({ tClient_ga4: ga4Data }); 165 } 166 // end SEM tracking 167 168 // Init quickview textboxes 169 if (site.siteConfig.custom[site.site].quickview.active) { 170 const textBoxes = document.querySelectorAll('[data-qv-enabled-textbox]'); 171 172 if (textBoxes.length) { 173 require(['plugins_common_custom_quickview_textbox'], function (qv) {
174 qv.init(); 175 }); 176 } 177 } 178}); 179 180// Lazy-load FontAwesome 181(function() { 182 var css = document.createElement('link'); 183 css.href = 'https://use.fontawesome.com/releases/v5.14.0/css/all.css'; 184 css.rel = 'stylesheet'; 185 css.type = 'text/css'; 186 document.getElementsByTagName('head')[0].appendChild(css); 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.