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