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 var eventCatName = widget.name; 28 switch (event.detailType) { 29 case "listings": 30 eventCatName = "Listings"; 31 break; 32 case "events": 33 eventCatName = "Events"; 34 break; 35 case "offers": 36 eventCatName = "Coupons"; 37 break; 38 default: 39 eventCatName = widget.name; 40 } 41 var data = { 42 eventCategory: eventCatName, 43 eventAction: event.name +" - "+ event.value, 44 eventLabel: event.refTitle, 45 ot: event.type, 46 ltxt: event.displayText, 47 nonInteraction: false 48 } 49 if (data.eventCategory && data.eventAction && data.eventLabel) 50 gtm.sendEvents({tClient: data}); 51 52 //ga4 53 let ga4Data = { 54 eventName: event.value ==="Add" ? "add_to_favorites" : undefined, 55 itemType: event.itemType ? event.itemType : (widget.name ==="Coupons"? 'offers' : widget.name.toLowerCase()), 56 itemName: event.itemName ?? event.refTitle, 57 linkObject: event.linkObject || event.value ==="Add" ? "Add to Favorites" : undefined, 58 itemCategory : event?.crmc 59 } 60 if (event?.crmsc) 61 ga4Data.itemSubcategory = event?.crmsc; 62 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 63 gtm.sendEvents({tClient_ga4: ga4Data}); 64 } 65 66 function handleQuickview(payload) { 67 var event = payload.eventContext, 68 widget = payload.widgetContext; 69 var eventCatName = widget.name; 70 switch (event.detailType) { 71 case "listings": 72 eventCatName = "Listings"; 73 break; 74 case "events": 75 eventCatName = "Events"; 76 break; 77 case "offers": 78 eventCatName = "Coupons"; 79 break; 80 default: 81 eventCatName = widget.name; 82 } 83 var data = { 84 eventCategory: eventCatName, 85 eventAction: event.linkTo, 86 eventLabel: event.refTitle, 87 ot: event.type, 88 ltxt: event.displayText, 89 nonInteraction: false 90 } 91 if (data.eventCategory && data.eventAction && data.eventLabel) 92 gtm.sendEvents({ tClient: data }); 93 } 94 95 function handleScrollIntoView(payload) { 96 var event = payload.eventContext, 97 widget = payload.widgetContext; 98 var data = { 99 eventCategory: widget.name, 100 eventAction: event.name, 101 eventLabel: "Widget - " + widget.name, 102 nonInteraction: true 103 } 104 if (data.eventCategory && data.eventAction && data.eventLabel) 105 gtm.sendEvents({ tClient: data }); 106 } 107 108 function handleShowMoreToggle(payload) { 109 var event = payload.eventContext, 110 widget = payload.widgetContext; 111 var data = { 112 eventCategory: widget.name, 113 eventAction: event.name, 114 eventLabel: event.displayText + " | " + event.refTitle, 115 nonInteraction: false 116 } 117 if (data.eventCategory && data.eventAction && data.eventLabel) 118 gtm.sendEvents({ tClient: data }); 119 // ga4 120 let ga4Data = { 121 eventName: "filter", 122 filterType: `${event?.refTitle} | ${event?.displayText}`, 123 itemType: event?.itemType 124 } 125 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 126 gtm.sendEvents({tClient_ga4: ga4Data}); 127 } 128 129 function handlePageChange(payload) { 130 var event = payload.eventContext, 131 widget = payload.widgetContext; 132 var data = { 133 eventCategory: widget.name, 134 eventAction: event.name, 135 eventLabel: event.refTitle + " | " + event.displayText, 136 nonInteraction: false 137 } 138 if (data.eventCategory && data.eventAction && data.eventLabel) 139 gtm.sendEvents({ tClient: data }); 140 // ga4 141 let ga4Data = { 142 eventName: "filter", 143 filterType: `${event?.refTitle} | ${event?.displayText}`, 144 itemType: event?.itemType
145 } 146 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 147 gtm.sendEvents({tClient_ga4: ga4Data}); 148 } 149 150 function handleItemClick(payload){ 151 var event = payload.eventContext, 152 widget = payload.widgetContext; 153 if(event.trackingType === "detail"){ 154 var data = { 155 eventCategory: widget.name, 156 eventAction: event.linkTo, 157 eventLabel: event.refTitle, 158 catName: event.catName, 159 crmc: event.crmc, 160 crmsc: event.crmsc, 161 region: event.region, 162 ua: event.ua, 163 vt: event.vt, 164 et: event.displayText, 165 address: event.address, 166 href: event.href, 167 lt: event.lt, 168 ot: event.type, 169 recId: event.recId, 170 nonInteraction: false 171 } 172 }else{ 173 var data = { 174 eventCategory: widget.name, 175 eventAction: event.linkTo, 176 eventLabel: event.refTitle, 177 ot: event.type, 178 crmc: event.crmc, 179 crmsc: event.crmsc, 180 address: event.address, 181 dtn: event.dtn, 182 et: event.refTitle, 183 row: event.row, 184 lt: event.lt, 185 recId: event.recId, 186 region: event.region, 187 ua: event.ua, 188 vt: event.vt, 189 nonInteraction: false 190 } 191 } 192 /* alter action for once there */ 193 if (event.recId !== undefined && event.recId.toString().indexOf("ot") > -1) { 194 data.eventAction = "OnceThere"; 195 } 196 if (data.eventCategory && data.eventAction && data.eventLabel) 197 gtm.sendEvents({tClient: data}); 198 199 // ga4 200 if (event?.name === "Quick View") { 201 var ga4Data = { 202 eventName: "filter", 203 filterType: "Open Quick View", 204 itemType: event.itemType, 205 itemName: event.itemName ?? event.refTitle, 206 } 207 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 208 gtm.sendEvents({tClient_ga4: ga4Data}); 209 } 210 else { 211 var ga4Data = { 212 eventName: event.eventName, 213 itemType: event.itemType, 214 itemName: event.itemName ?? event.refTitle, 215 linkObject: event.linkObject, 216 itemCategory: event?.crmc 217 } 218 if (event?.crmsc) 219 ga4Data.itemSubcategory = event?.crmsc; 220 if (event.partnerReferralData) 221 ga4Data = {...ga4Data, ...event.partnerReferralData, value: 5}; 222 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 223 gtm.sendEvents({tClient_ga4: ga4Data}); 224 } 225 } 226 227 function handleMapPinClick(payload) { 228 var event = payload.eventContext, 229 widget = payload.widgetContext; 230 var data = { 231 eventCategory: widget.name, 232 eventAction: event.name, 233 eventLabel: event.refTitle, 234 ot: event.type, 235 nonInteraction: false 236 } 237 if (data.eventCategory && data.eventAction && data.eventLabel) 238 gtm.sendEvents({ tClient: data }); 239 } 240 241 function handleFilterChange(payload) { 242 var event = payload.eventContext, 243 widget = payload.widgetContext; 244 var data = { 245 eventCategory: widget.name, 246 eventAction: "Filter Change", 247 eventLabel: event.refTitle + " | " + event.displayText, 248 nonInteraction: false 249 } 250 if (data.eventCategory && data.eventAction && data.eventLabel) 251 gtm.sendEvents({ tClient: data }); 252 // ga4 253 let ga4Data = { 254 eventName: "filter", 255 filterType: `${event?.refTitle} | ${event?.displayText}`, 256 itemType: event?.itemType 257 } 258 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 259 gtm.sendEvents({tClient_ga4: ga4Data}); 260 } 261 // end SEM tracking 262}); 263 264// Lazy-load FontAwesome 265(function () { 266 var css = document.createElement('link'); 267 css.href = 'https://use.fontawesome.com/releases/v5.14.0/css/all.css'; 268 css.rel = 'stylesheet'; 269 css.type = 'text/css'; 270 document.getElementsByTagName('head')[0].appendChild(css); 271})(); 272 273// Lazy-load js for widgets that have the [data-js] attribute on their root node 274(function () { 275 function initDataJs(searchScope, selector = '[data-js]') { 276 require(['plugins_common_custom_lazyload'], function (lazyload) { 277 var widgetsToLoad = []; 278 let scope = searchScope ? searchScope : document; 279 var els = scope.querySelectorAll(selector); 280 281 for (var i = 0; i < els.length; i++) { 282 widgetsToLoad.push(els[i]); 283 } 284 285 lazyload.lazy(widgetsToLoad, function (entry, io) { 286 var target = entry.target; 287 var jsFile = target.dataset.js; 288 var guid = target.dataset.guid; 289 290 require([jsFile], function (widget) { 291 widget.init(guid); 292 io.unobserve(target); 293 }); 294 }); 295 }); 296 }; 297 298 initDataJs(); 299 300 let eventToSelectorMap = { 301 // each event needs a slightly different selector 302 "dynamicContentLoaded": "[data-dynamic-chosen] > .contentRender > [data-js]", 303 "abContentLoaded": "[data-ab-chosen] > .contentRender > [data-js]" 304 } 305
306 Object.entries(eventToSelectorMap).forEach(event => { 307 const [eventName, selector] = event; 308 window.addEventListener(eventName, e => { 309 const collections = document.querySelector(`[data-guid="${e}"]`); 310 initDataJs(collections, selector); 311 }); 312 }); 313})(); 314 315// Remove critical styles from head after all CSS files have loaded 316// This prevents a situation where the critical styles cause an override of the CSS in files 317const criticalStyles = document.querySelector('[data-critical-styles]'); 318if (criticalStyles) { 319 window.addEventListener('load', e => criticalStyles.remove()); 320}
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.