1 2 3/* 4 * MOTOINSIGHT INTEGRATION SCRIPT 5 */ 6 7var MOTOINSIGHT = MOTOINSIGHT || {}; 8 9(function (context) { 10 11 12 const protocol = 'https://'; 13 const inventoryDataUrl = protocol + 'shop.mississaugatoyota.com/integration/inventory_data'; 14 const loginDataUrl = protocol + 'shop.mississaugatoyota.com/integration/login_data'; 15 const configUrl = protocol + 'shop.mississaugatoyota.com/integration/integration_script_config'; 16 17 const localInventoryData = {}; 18 let config = {} 19 let configInitialized = false; 20 let initialLanguage = "en-ca"; 21 22 function ajax(cfg) { 23 const xhr = new XMLHttpRequest(); 24 xhr.open('GET', cfg.url, true); 25 xhr.onload = function () { 26 cfg.success(JSON.parse(xhr.responseText)) 27 }; 28 xhr.withCredentials = cfg.withCredentials; 29 xhr.send(); 30} 31 32 function getDefaultLangQueryParam() { 33 const urlSearchParams = new URLSearchParams(window.location.search); 34 return urlSearchParams.get('defaultLang') ?? ""; 35} 36 37function getUrlForIntegrationScriptInit() { 38 const params = {}; 39 const defaultLang = getDefaultLangQueryParam(); 40 41 if(defaultLang){ 42 params["defaultLang"] = defaultLang; 43 } 44 const queryString = new URLSearchParams(params).toString(); 45 46 return queryString ? `?${queryString}` : ''; 47} 48 49 const CTACopy = (function () { 50 let widgetsCopies = {}; 51 52 const initWidgetCopies = function(newWidgetCopies) { 53 widgetsCopies = newWidgetCopies; 54 }; 55 56 const hasWidgetCopies = function(){ 57 return !!Object.keys(widgetsCopies).length; 58 }; 59 60// 61 const getCopy = function (elem, widgetLink, languagePriority) { 62 if (hasWidgetCopies()) { 63 let copies = null; 64 for (const language of languagePriority) { 65 copies = widgetsCopies[language]; 66 if (copies) { 67 break; 68 } 69 } 70 if (copies && copies[widgetLink]) { 71 return copies[widgetLink]; 72 } 73 } 74 return elem.innerText; 75 }; 76 77 const setCopy = function (elem, copy) { 78 if (elem.innerText === copy) { 79 return; 80 } 81 for (const child of elem.childNodes) { 82 if (child.nodeType === Node.TEXT_NODE && child.textContent.trim()) { 83 child.textContent = copy; 84 } else { 85 setCopy(child, copy); 86 } 87 } 88 }; 89 return { initWidgetCopies, hasWidgetCopies, getCopy, setCopy }; 90})(); 91 92 const PageDestinations = (function () { 93 94 const pagesConfigs = { 95 credit: { 96 widgetLink: 'credit_app_url', 97 campaign: 'creditapp', 98 }, 99 purchase: { 100 widgetLink: 'purchase_url', 101 campaign: 'transaction', 102 }, 103 trade_in: { 104 widgetLink: 'trade_in_url', 105 campaign: 'tradein', 106 }, 107 deposit: { 108 widgetLink: 'deposit_url', 109 campaign: 'deposit', 110 }, 111 vdp: { 112 widgetLink: 'vdp_url', 113 campaign: 'vdp', 114 }, 115 appointment: { 116 widgetLink: 'appointment_url', 117 campaign: 'appointment', 118 }, 119 }; 120 let defaultPage; 121 const ITM_MEDIUMS = { 122 VDP: 'vdp', 123 VLP: 'vlp', 124 }; 125 126 function initDefaultPage(newDefaultPage){ 127 defaultPage = newDefaultPage; 128 // identify data for the default page 129 for (const key in pagesConfigs) { 130 if (pagesConfigs[key].widgetLink === defaultPage) { 131 pagesConfigs['default'] = pagesConfigs[key]; 132 break; 133 } 134 } 135 } 136 137 138// 139 function getConfig(elem, itemData) { 140 const page = elem.getAttribute('data-mc-page') || 'default'; 141 const config = pagesConfigs[page]; 142 if (!config || !itemData[config.widgetLink]) { 143 return pagesConfigs['default']; 144 } 145 return config; 146 } 147 148// 149 function getURL({baseUrl, campaign, copy, medium, localInventoryData}) { 150 const defaultMedium = Object.keys(localInventoryData).length > 1 ? ITM_MEDIUMS.VLP : ITM_MEDIUMS.VDP; 151 copy = copy.replaceAll(' ', '+'); 152 153 url = new URL(baseUrl) 154 if (url.search === ""){ 155 url.search = "?" 156 } else { 157 url.search = url.search + "&" 158 } 159 url.search = url.search + `itm_source=${location.host}&itm_medium=${medium || defaultMedium}&itm_campaign=${campaign}&itm_content=${copy}` 160 161 return url.href 162 } 163 function getDefaultPage(){ 164 return defaultPage; 165 } 166 return { initDefaultPage, getConfig, getDefaultPage, getURL }; 167})(); 168 169 170 171 function getDefaultLanguage(){ 172 return config["default_language"]; 173 } 174 function getWidgetsCTA(){ 175 return config["widgets_cta"]; 176 } 177 function getDefaultWidgetLink(){ 178 return config["default_widget_link"] 179 } 180 function getSettingsLanguage(){ 181 return config["settings_language"]; 182 } 183 184 function isDealerWebsiteWidgetActive(){ 185 return config["dealer_website_widget_active"]; 186 } 187 188 function doAttachClickEvent(elem){ 189 return !(elem.tagName === 'A' && !(elem.hasAttribute('data-mc-target') || elem.hasAttribute('data-mc-purchase-url'))); 190 } 191 192 function getLinkAttributeName(elem){ 193 return (elem.tagName === 'A' && ! elem.hasAttribute('data-mc-purchase-url')) ? 'href' : 'data-mc-purchase-url'; 194 } 195 196 function openLink(url, mode){ 197 if(["blank", "_blank", "tab"].indexOf(mode) !== -1){ 198 window.open(url, '_blank'); 199 } else { 200 window.location.href = url; 201 } 202 } 203 204 function updateInventoryData(data) { 205 for (const uid in data) { 206 localInventoryData[uid] = data[uid]; 207 } 208 } 209 210 function updateInventory(links, language) { 211 links.forEach(function (elem) { 212 213 const uid = elem.dataset.stockId; 214 const itemData = localInventoryData[uid]; 215 if (itemData && PageDestinations.getDefaultPage() in itemData) { 216 const linkAttributeName = getLinkAttributeName(elem); 217 const pageConfig = PageDestinations.getConfig(elem, itemData); 218 const copy = CTACopy.getCopy(elem, pageConfig.widgetLink, [language, getDefaultLanguage(), getSettingsLanguage()]); 219 const url = PageDestinations.getURL({ 220 baseUrl: itemData[pageConfig.widgetLink], 221 campaign: pageConfig.campaign, 222 copy, 223 medium: elem.getAttribute('data-mc-medium'), 224 localInventoryData, 225 }); 226 227 elem.setAttribute(linkAttributeName, url); 228 CTACopy.setCopy(elem, copy); 229 if(doAttachClickEvent(elem) && !elem.hasAttribute('data-mc-listener')) { 230 elem.addEventListener('click', function(event){ 231 event.preventDefault(); 232 event.stopPropagation(); 233 const mode = event.target.getAttribute('data-mc-target') || 'blank'; 234 openLink(url, mode); 235 }); 236 elem.setAttribute('data-mc-listener', true); 237 } 238 if (elem.style.display === 'none') { 239 elem.style.display = ''; 240 } 241 } else { 242 elem.style.display = 'none'; 243 } 244 }); 245 } 246 247 function updateLoginWidget(data) { 248 const widget = document.querySelector('.motocommerce-login-widget'); 249 const label = widget.querySelector('.motocommerce-login-label'); 250 const link = widget.querySelector('.motocommerce-login-link'); 251 const counter = widget.querySelector('.motocommerce-order-counter'); 252 253 link.setAttribute('href', data['link_url']); 254 if (data['is_logged_in']) { 255 label.innerHTML = data['user_details']['name']; 256 counter.innerHTML = data['order_details']['count']; 257 } 258 } 259 260 function fetchInventoryData(language) { 261 const stockIdList = []; 262 263 const links = document.querySelectorAll('.motocommerce-vehicle-link'); 264 links.forEach(function (elem) { 265 const stockId = elem.dataset.stockId; 266 if (stockId && !stockIdList.includes(stockId)) { 267 stockIdList.push(stockId); 268 } 269 }); 270 if (stockIdList.length) { 271 const lang = language || getDefaultLanguage(); 272 const invItems = stockIdList.join('&inventory_items='); 273 ajax({ 274 url: `${inventoryDataUrl}?lang=${lang}&inventory_items=${invItems}`, 275 success: function (data) { 276 if (!('error' in data)) { 277 updateInventoryData(data); 278 updateInventory(links, language); 279 } 280 } 281 }); 282 } else { 283 // We still need to update the DOM when we have all stock IDs cached 284 updateInventory(links, language); 285 } 286 } 287 288 function fetchLoginData() { 289 if (document.querySelector('.motocommerce-login-widget')) { 290 ajax({ 291 url: loginDataUrl, 292 // We require the cookies to be sent to fetch session information 293 withCredentials: true, 294 success: updateLoginWidget 295 }); 296 } 297 } 298 299 function hideWidgets() { 300 document.querySelectorAll('.motocommerce-vehicle-link').forEach(function (node) { 301 node.style.display = 'none'; 302 }) 303 } 304 305 context.updateView = function (language) { 306 if(!configInitialized){ 307 initialLanguage = language; 308 return; 309 } 310 if (isDealerWebsiteWidgetActive()){ 311 const lang = language || initialLanguage; 312 fetchInventoryData(lang); 313 } 314 else{ 315 hideWidgets(); 316 } 317 fetchLoginData(); 318 }; 319
320 function init(){ 321 ajax({ 322 url: configUrl + getUrlForIntegrationScriptInit(), 323 // We require the cookies to be sent to fetch session information 324 withCredentials: true, 325 success: function (data) { 326 config = {...config, ...data}; 327 configInitialized = true; 328 context.updateView(getDefaultLanguage()); 329 CTACopy.initWidgetCopies(getWidgetsCTA()); 330 PageDestinations.initDefaultPage(getDefaultWidgetLink()); 331 } 332 }); 333 } 334 335 document.addEventListener("DOMContentLoaded", init); 336 if (document.readyState !== "loading") { 337 init() 338 } 339 340})(MOTOINSIGHT);
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.