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