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 8 9 $('.menu-action').on('click', function() { 10 var t = $(this); 11 var active = t.hasClass('active'); 12 13 if ( t.closest('ul').hasClass('nav-section') ) { 14 $('.top-level > .nav-item-text .menu-action, .top-level > .nav-item-text a, .top-level > ul').removeClass('active'); 15 $('.sub-nav-mobile .menu-action, .sub-nav-mobile a, .sub-nav-mobile ul').removeClass('active'); 16 } 17 if ( t.closest('li').hasClass('second-level') ) { 18 $('.sub-nav-mobile .menu-action, .sub-nav-mobile a, .sub-nav-mobile ul').removeClass('active'); 19 } 20 if (active) { 21 t.removeClass('active'); 22 t.closest('li').children('ul').removeClass('active'); 23 t.siblings().removeClass('active'); 24 } else { 25 t.addClass('active'); 26 t.closest('li').children('ul').addClass('active'); 27 t.siblings().addClass('active'); 28 } 29 }); 30 31 $('.menu-tab-toggler').on('click', function(){ 32 $('.menu-tab-toggler').removeClass('active'); 33 $(this).addClass('active'); 34 $('.menu-tab-panel').removeClass('active'); 35 var id = $(this).attr('data-id'); 36 $('.'+id+'-panel').addClass('active'); 37 }); 38 39 $('.mobile-nav-action').on('click', function() { 40 $('.mobile-nav-dropdown .action-content .top-content').css("background-image", "url(//assets.simpleviewinc.com/simpleview/image/upload/v1/clients/toronto/search_bg_e600bf60-55e6-40f1-b271-c74ecf85f867.jpg)"); 41 $(this).toggleClass('active'); 42 $(this).siblings().toggleClass('active'); 43 }); 44 45 46 gtm.update(); 47 48 // SEM tracking 49 var uiEvents = new UIWatcher({}, $, document); 50 uiEvents.on("change-page", handlePageChange); 51 uiEvents.on("view-change", handleFilterChange); 52 uiEvents.on("sort-change", handleFilterChange); 53 uiEvents.on("value-change", handleFilterChange); 54 uiEvents.on("toggle-show-more", handleShowMoreToggle); 55 uiEvents.on("scroll-into-view", handleScrollIntoView); 56 uiEvents.on("item-click", handleItemClick); 57 uiEvents.on("map-pin-click", handleMapPinClick); 58 uiEvents.on("tripbuilder", handleTripbuilder); 59 uiEvents.on("quick-view", handleQuickview); 60 61 // tracking handlers 62 function handleTripbuilder(payload){ 63 var event = payload.eventContext, 64 widget = payload.widgetContext; 65 var data = { 66 eventCategory: widget.name, 67 eventAction: event.name +" - "+ event.value, 68 eventLabel: event.refTitle, 69 ot: event.type, 70 ltxt: event.displayText, 71 nonInteraction: false 72 } 73 if (data.eventCategory && data.eventAction && data.eventLabel) 74 gtm.sendEvents({tClient: data}); 75 } 76 77 function handleQuickview(payload){ 78 var event = payload.eventContext, 79 widget = payload.widgetContext; 80 var eventCatName = widget.name; 81 switch(event.detailType) { 82 case "listings": 83 eventCatName = "Listings"; 84 break; 85 case "events": 86 eventCatName = "Events"; 87 break; 88 case "offers": 89 eventCatName = "Coupons"; 90 break; 91 default: 92 eventCatName = widget.name; 93 } 94 var data = { 95 eventCategory: eventCatName, 96 eventAction: event.linkTo, 97 eventLabel: event.refTitle, 98 ot: event.type, 99 ltxt: event.displayText, 100 nonInteraction: false 101 } 102 let ga4Data = { 103 eventName: event.eventName, 104 itemType: event.itemType, 105 itemName: event.itemName ?? event.refTitle, 106 linkObject: event.linkObject, 107 crmCatSubcat: event.crmc 108 } 109 if (data.eventCategory && data.eventAction && data.eventLabel) 110 gtm.sendEvents({tClient: data}); 111 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 112 gtm.sendEvents({tClient_ga4: ga4Data}); 113 } 114 115 function handleScrollIntoView(payload){ 116 var event = payload.eventContext, 117 widget = payload.widgetContext; 118 var data = { 119 eventCategory: widget.name, 120 eventAction: event.name, 121 eventLabel: "Widget - "+widget.name, 122 nonInteraction: true 123 } 124 if (data.eventCategory && data.eventAction && data.eventLabel) 125 gtm.sendEvents({tClient: data}); 126 } 127 128 function handleShowMoreToggle(payload){ 129 var event = payload.eventContext, 130 widget = payload.widgetContext; 131 var data = { 132 eventCategory: widget.name, 133 eventAction: event.name, 134 eventLabel: event.displayText+" | "+event.refTitle, 135 nonInteraction: false 136 } 137 let ga4Data = { 138 eventName: "filter", 139 filterType: event.displayText+" | "+event.refTitle, 140 itemType: event.itemType
141 } 142 if (data.eventCategory && data.eventAction && data.eventLabel) 143 gtm.sendEvents({tClient: data}); 144 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 145 gtm.sendEvents({tClient_ga4: ga4Data}); 146 } 147 148 function handlePageChange(payload){ 149 var event = payload.eventContext, 150 widget = payload.widgetContext; 151 var data = { 152 eventCategory: widget.name, 153 eventAction: event.name, 154 eventLabel: event.refTitle+" | "+event.displayText, 155 nonInteraction: false 156 } 157 let ga4Data = { 158 eventName: "filter", 159 filterType: event.refTitle+" | "+event.displayText, 160 itemType: event.itemType 161 } 162 if (data.eventCategory && data.eventAction && data.eventLabel) 163 gtm.sendEvents({tClient: data}); 164 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 165 gtm.sendEvents({tClient_ga4: ga4Data}); 166 } 167 168 function handleItemClick(payload){ 169 var event = payload.eventContext, 170 widget = payload.widgetContext; 171 var data = { 172 eventCategory: widget.name, 173 eventAction: event.linkTo, 174 eventLabel: event.refTitle, 175 ot: event.type, 176 ltxt: event.displayText, 177 nonInteraction: false 178 } 179 /* alter action for once there */ 180 if (event.recId !== undefined && event.recId.toString().indexOf("ot") > -1) { 181 data.eventAction = "OnceThere"; 182 } 183 if (data.eventCategory && data.eventAction && data.eventLabel) 184 gtm.sendEvents({tClient: data}); 185 let crmCatSubcat = event?.crmc; 186 if(event?.v_crmc){ 187 crmCatSubcat = event.v_crmc; 188 } else if (crmCatSubcat && event.crmsc) { 189 crmCatSubcat += " | " + event.crmsc; 190 } 191 if (event?.name === "Quick View") { 192 var ga4Data = { 193 eventName: "filter", 194 filterType: "Open Quick View", 195 itemType: event.itemType, 196 itemName: event.itemName ?? event.refTitle, 197 } 198 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 199 gtm.sendEvents({tClient_ga4: ga4Data}); 200 } else { 201 var ga4Data = { 202 eventName: event.eventName, 203 itemType: event.itemType, 204 itemName: event.itemName ?? event.refTitle, 205 linkObject: event.linkObject, 206 crmCatSubcat: crmCatSubcat 207 } 208 if (event.itemType === 'events' && event.eventName === 'link_to_detail') { 209 ga4Data.eventName = 'partner_referral'; 210 } 211 if (ga4Data.eventName && ga4Data.itemType && ga4Data.itemName && ga4Data.linkObject) 212 gtm.sendEvents({tClient_ga4: ga4Data}); 213 } 214 } 215 216 function handleMapPinClick(payload){ 217 var event = payload.eventContext, 218 widget = payload.widgetContext; 219 var data = { 220 eventCategory: widget.name, 221 eventAction: event.name, 222 eventLabel: event.refTitle, 223 ot: event.type, 224 nonInteraction: false 225 } 226 if (data.eventCategory && data.eventAction && data.eventLabel) 227 gtm.sendEvents({tClient: data}); 228 } 229 230 function handleFilterChange (payload) { 231 var event = payload.eventContext, 232 widget = payload.widgetContext; 233 var data = { 234 eventCategory: widget.name, 235 eventAction: "Filter Change", 236 eventLabel: event.refTitle+" | "+event.displayText, 237 nonInteraction: false 238 } 239 let ga4Data = { 240 eventName: "filter", 241 filterType: event.refTitle+" | "+event.displayText, 242 itemType: event.itemType 243 } 244 if (data.eventCategory && data.eventAction && data.eventLabel) 245 gtm.sendEvents({tClient: data}); 246 if (ga4Data.eventName && ga4Data.filterType && ga4Data.itemType) 247 gtm.sendEvents({tClient_ga4: ga4Data}); 248 } 249 // end SEM tracking 250}); 251 252// Lazy-load FontAwesome 253(function() { 254 var css = document.createElement('link'); 255 css.href = 'https://use.fontawesome.com/releases/v5.14.0/css/all.css'; 256 css.rel = 'stylesheet'; 257 css.type = 'text/css'; 258 document.getElementsByTagName('head')[0].appendChild(css); 259})(); 260 261 262// Lazy-load js for widgets that have the [data-js] attribute on their root node 263(function() { 264 function initDataJs(searchScope, selector = '[data-js]' ){ 265 require(['plugins_common_custom_lazyload'], function(lazyload) { 266 var widgetsToLoad = []; 267 let scope = searchScope ? searchScope : document; 268 var els = scope.querySelectorAll(selector); 269 for (var i = 0; i < els.length; i++) { 270 widgetsToLoad.push(els[i]); 271 } 272 273 lazyload.lazy(widgetsToLoad, function(entry, io) { 274 var target = entry.target; 275 var jsFile = target.dataset.js; 276 var guid = target.dataset.guid; 277 278 require([jsFile], function(widget) { 279 widget.init(guid); 280 io.unobserve(target); 281 }); 282 }); 283 }); 284 }; 285 286 initDataJs(); 287 288 let eventToSelectorMap = { 289 // each event needs a slightly different selector 290 "dynamicContentLoaded": "[data-dynamic-chosen] [data-js]", 291 "abContentLoaded": "[data-ab-chosen] [data-js]" 292 } 293
294 Object.entries(eventToSelectorMap).forEach( event => { 295 const [eventName, selector] = event; 296 window.addEventListener(eventName, e => { 297 const collections = document.querySelector(`[data-guid="${e}"]`); 298 initDataJs(collections, selector); 299 }); 300 }); 301})(); 302 303// Wrap core_textbox iframes to prevent horizontal overflow 304(function() { 305 const textBoxes = document.querySelectorAll('[data-sv-core-textbox]'); 306 if (textBoxes.length) { 307 require(['plugins_common_custom_utils'], function(utils) { 308 textBoxes.forEach(utils.wrapIframes); 309 }); 310 } 311})(); 312 313// Remove critical styles from head after all CSS files have loaded 314// This prevents a situation where the critical styles cause an override of the CSS in files 315const criticalStyles = document.querySelector('[data-critical-styles]'); 316if (criticalStyles) { 317 window.addEventListener('load', e => criticalStyles.remove()); 318}
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.