1var PMDatalayer = {}; 2 3(function () { 4 PMDatalayer = { 5 id: "pixelmotion", 6 name: "Pixelmotion Datalayer", 7 log_for: "pixelmotion", 8 parent: {}, 9 sessionId: null,//sessionId is also visitorId for api calls 10 sessionData: {}, 11 viewedVins: [],//synced with cookie 12 savedVins: [],//synced with cookie 13 submittedVins: [],//synced with cookie 14 15 onRegistry: function () { 16 return true; 17 }, 18 19 init: function () { 20 // Check if we need to clear visitor data 21 let urlParams = new URLSearchParams(window.location.search); 22 if (urlParams.get('pmClearVisitorData') === '1') { 23 PMDatalayer.clearVisitorData(); 24 } 25 26 PMDatalayer.sessionId = this.getSessionId(); 27 PMDatalayer.sessionData = { 28 sessionId: PMDatalayer.sessionId, 29 platform: navigator.platform, 30 userAgent: navigator.userAgent, 31 }; 32 PMDatalayer.loadVinArrays(); 33 }, 34 35 storePmOfferId: function() { 36 var urlParams = new URLSearchParams(window.location.search); 37 var pmOfferId = urlParams.get('pmOfferId') || urlParams.get('pmOfferid'); 38 39 if (pmOfferId) { 40 sessionStorage.setItem('pmOfferId', pmOfferId); 41 } 42 }, 43 44 generateSessionId: function () { 45 var timestamp = new Date().getTime(); 46 var randomPart = Math.floor(Math.random() * 10000); // Add randomness for uniqueness 47 return `${timestamp}${randomPart}`; 48 }, 49 50 setCookie(name, value, exdays) { 51 var d = new Date(); 52 d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); 53 document.cookie = name + "=" + value + ";expires=" + d.toUTCString() + ";path=/"; 54 }, 55 56 setSessionId() { 57 let sessionId = PMDatalayer.generateSessionId(); 58 PMDatalayer.setCookie("sessionId", sessionId, 1); 59 }, 60 61 getSessionData() { 62 if (PMDatalayer.sessionData.sessionId == null) { 63 PMDatalayer.sessionId = this.getSessionId(); 64 PMDatalayer.sessionData.sessionId = PMDatalayer.sessionId; 65 } 66 return PMDatalayer.sessionData; 67 }, 68 69 getSessionId() { 70 var name = "sessionId="; 71 var decodedCookie = decodeURIComponent(document.cookie); 72 var ca = decodedCookie.split(";"); 73 var sessionId = ""; 74 for (var i = 0; i < ca.length; i++) { 75 var c = ca[i]; 76 while (c.charAt(0) == " ") { 77 c = c.substring(1); 78 } 79 if (c.indexOf(name) == 0) { 80 sessionId = c.substring(name.length, c.length); 81 break; 82 } 83 } 84 if (sessionId == "" || sessionId == undefined){ 85 return this.setSessionId() 86 } 87 return sessionId; 88 }, 89 90 loadVinArrays() { 91 PMDatalayer.viewedVins = PMDatalayer.getVinArrayFromCookie("viewedVins"); 92 PMDatalayer.savedVins = PMDatalayer.getVinArrayFromCookie("savedVins"); 93 PMDatalayer.submittedVins = PMDatalayer.getVinArrayFromCookie("submittedVins"); 94 }, 95 96 getVinArrayFromCookie(cookieName) { 97 var name = cookieName + "="; 98 var decodedCookie = decodeURIComponent(document.cookie); 99 var ca = decodedCookie.split(";"); 100 for (var i = 0; i < ca.length; i++) { 101 var c = ca[i]; 102 while (c.charAt(0) == " ") { 103 c = c.substring(1); 104 } 105 if (c.indexOf(name) == 0) { 106 try { 107 return JSON.parse(c.substring(name.length, c.length)); 108 } catch(e) { 109 return []; 110 } 111 } 112 } 113 return []; 114 }, 115 116 setVinArrayCookie(cookieName, vinArray) { 117 PMDatalayer.setCookie(cookieName, JSON.stringify(vinArray), 30); 118 }, 119 120 addVin(vin, type) { 121 PMDatalayer.loadVinArrays(); 122 123 if (!vin || !type) { 124 console.error("addVin requires vin and type parameters"); 125 return false;
126 } 127 128 type = type.toLowerCase(); 129 let validTypes = ["viewed", "saved", "submitted"]; 130 if (!validTypes.includes(type)) { 131 console.error("Invalid type. Must be one of: " + validTypes.join(", ")); 132 return false; 133 } 134 135 let arrayName = type + "Vins"; 136 let array = PMDatalayer[arrayName]; 137 138 if (!array.includes(vin)) { 139 array.push(vin); 140 PMDatalayer.setVinArrayCookie(arrayName, array); 141 return true; 142 } 143 return false; 144 }, 145 146 /** 147 * Drops a vin from its cookie array. Counterpart of addVin: without it the arrays only ever 148 * grow and keep reporting vehicles the visitor removed (unsaved vehicle, cleared view). 149 */ 150 removeVin(vin, type) { 151 PMDatalayer.loadVinArrays(); 152 153 if (!vin || !type) { 154 console.error("removeVin requires vin and type parameters"); 155 return false; 156 } 157 158 type = type.toLowerCase(); 159 let validTypes = ["viewed", "saved", "submitted"]; 160 if (!validTypes.includes(type)) { 161 console.error("Invalid type. Must be one of: " + validTypes.join(", ")); 162 return false; 163 } 164 165 let arrayName = type + "Vins"; 166 let array = PMDatalayer[arrayName]; 167 let index = array.indexOf(vin); 168 169 if (index > -1) { 170 array.splice(index, 1); 171 PMDatalayer.setVinArrayCookie(arrayName, array); 172 return true; 173 } 174 return false; 175 }, 176 177 getVisitorVins() { 178 if (pm_datalayer_data.enable_personalization !== '1') { 179 return []; 180 } 181 182 let allVins = []; 183 184 // Add submitted VINs first 185 if (PMDatalayer.submittedVins && PMDatalayer.submittedVins.length > 0) { 186 allVins = allVins.concat(PMDatalayer.submittedVins); 187 } 188 189 // Add saved VINs second 190 if (PMDatalayer.savedVins && PMDatalayer.savedVins.length > 0) { 191 allVins = allVins.concat(PMDatalayer.savedVins); 192 } 193 194 // Add viewed VINs last 195 if (PMDatalayer.viewedVins && PMDatalayer.viewedVins.length > 0) { 196 allVins = allVins.concat(PMDatalayer.viewedVins); 197 } 198 199 return allVins; 200 }, 201 202 clearVisitorData() { 203 // Clear the sessionId cookie 204 PMDatalayer.setCookie("sessionId", "", -1); 205 // Clear the VINs arrays cookies 206 PMDatalayer.setCookie("viewedVins", "", -1); 207 PMDatalayer.setCookie("savedVins", "", -1); 208 PMDatalayer.setCookie("submittedVins", "", -1); 209 210 // Reset in-memory arrays 211 PMDatalayer.viewedVins = []; 212 PMDatalayer.savedVins = []; 213 PMDatalayer.submittedVins = []; 214 PMDatalayer.clearCache(); 215 216 // Generate and set a new sessionId 217 PMDatalayer.setSessionId(); 218 219 // Clear saved vehicles from localStorage, check vehicle-save.js 220 localStorage.removeItem('pm_save_' + pm_api.site_token); 221 222 console.log("Visitor data cleared and new session started"); 223 }, 224 225 clearCache() { 226 $.ajax({ 227 type: "GET", 228 url: window.location.origin + "/pm/clearCache/clearVisitor", 229 success: function (response) { 230 console.log(response); 231 }, 232 }); 233 }, 234 235 events: { 236 pageView: function (args) { 237 console.log("PM Datalayer: pageview"); 238 let utilities = DataLayer.utilities; 239 let pageType = utilities.pageType; 240 let filters = {}; 241 let data = {}; 242 let url = window.location.href; 243 244 switch (pageType) { 245 case "Home": 246 data = { 247 event: "Entered Home Page", 248 section: "Home Page", 249 data: { 250 url: url, 251 }, 252 sessionData: PMDatalayer.sessionData, 253 siteToken: pm_api.site_token, 254 }; 255 break; 256
257 // VDP 258 case "Vehicle Details": 259 if ("VDP" in window) { 260 let vehicle = VDP.store.vehicle; 261 if (!vehicle) return; 262 data = { 263 event: "vdpView", 264 section: "VDP", 265 data: { 266 url: url, 267 }, 268 vehicle: { 269 vin: vehicle.vin, 270 condition: vehicle.condition, 271 year: vehicle.year, 272 make: vehicle.make, 273 model: vehicle.model, 274 trim: vehicle.trim ?? "", 275 }, 276 sessionData: PMDatalayer.sessionData, 277 siteToken: pm_api.site_token, 278 }; 279 } 280 break; 281 282 case "Vehicle Listing": 283 data = { 284 event: "vlpView", 285 section: "VLP Inventory", 286 data: { 287 url: url, 288 }, 289 sessionData: PMDatalayer.sessionData, 290 siteToken: pm_api.site_token, 291 }; 292 break; 293 default: 294 break; 295 } 296 297 data.sessionData = PMDatalayer.getSessionData(); 298 console.log(data); 299 PMDatalayer.saveDrsEvent(data); 300 }, 301 302 typedSearch: function (args) { 303 console.log("PM Datalayer typedSearch"); 304 let stringFilters = ""; 305 let filters = VLP.getFilters(); 306 stringFilters = Object.entries(filters) 307 .map(([key, value]) => `${key}:${value}`) 308 .join("|"); 309 let data = { 310 event: "typedSearch", 311 section: "VLP", 312 data: { 313 searchTerm: args.content, 314 searchResultsCount: args.count, 315 filters: stringFilters, 316 }, 317 sessionData: PMDatalayer.sessionData, 318 siteToken: pm_api.site_token, 319 }; 320 PMDatalayer.saveDrsEvent(data); 321 }, 322 323 filterSearch: function (args) { 324 console.log("PM Datalayer filterSearch"); 325 let filters = VLP.getFilters(); 326 let stringFilters = ""; 327 stringFilters = Object.entries(filters) 328 .map(([key, value]) => `${key}:${value}`) 329 .join("|"); 330 data = { 331 event: "filterSearch", 332 section: "Inventory", 333 data: { 334 filters: stringFilters, 335 url: VLP.createNewUrl(1), 336 searchResultsCount: args.count, 337 }, 338 sessionData: PMDatalayer.sessionData, 339 siteToken: pm_api.site_token, 340 }; 341 PMDatalayer.saveDrsEvent(data); 342 }, 343 344 generalCTAClick: function (args) { 345 console.log("PM Datalayer cta click"); 346 let id = PMDatalayer.parent.utilities.element.getAttribute("id") || ""; 347 348 let data = { 349 event: "generalCTAClick", 350 data: { 351 elementText: args.text, 352 action: "buttonClick", 353 linkText: id, 354 }, 355 vehicle: null, 356 sessionData: PMDatalayer.sessionData, 357 siteToken: pm_api.site_token, 358 }; 359 360 if(args.vehicle){ 361 data.vehicle = { 362 vin: args.vehicle.vin, 363 condition: args.vehicle.condition, 364 year: args.vehicle.year, 365 make: args.vehicle.make, 366 model: args.vehicle.model, 367 trim: args.vehicle.trim, 368 } 369 } 370 let ctaData = { 371 sessionId: PMDatalayer.sessionData.sessionId, 372 siteToken: pm_api.site_token, 373 }; 374 PMDatalayer.saveDrsEvent(data); 375 // PMDatalayer.trackCTADRA(ctaData); 376 }, 377 378 formInitiation: function (args) { 379 console.info("PM Datalayer formInitiation");
380 let formType = PMDatalayer.parent.fixFormTypeShift(args.formType); 381 let data = { 382 formTitle: formType, 383 displayType: args.displayType, 384 vehicle: null, 385 sessionData: PMDatalayer.sessionData, 386 siteToken: pm_api.site_token, 387 }; 388 389 if(args.vehicle){ 390 data.vehicle= { 391 vin: args.vehicle.vin, 392 condition: args.vehicle.condition, 393 year: args.vehicle.year, 394 make: args.vehicle.make, 395 model: args.vehicle.model, 396 trim: args.vehicle.trim, 397 } 398 } 399 PMDatalayer.saveDrsEvent(data); 400 }, 401 402 clickToCall: function (args) { 403 console.info("PM Datalayer: clickToCall"); 404 let number = PMDatalayer.parent.utilities.element.getAttribute("href"); 405 406 let data = { 407 event: "clickToCall", 408 number: number, 409 data: [], 410 type: args.clickToCallDepartment, 411 sessionData: PMDatalayer.sessionData, 412 siteToken: pm_api.site_token, 413 }; 414 415 PMDatalayer.saveDrsEvent(data); 416 }, 417 }, 418 419 trackCTADRA: function(data) { 420 if (!pm_datalayer_data.allow_pmdrs) return; 421 let path = pm_api_customers.path + "customerJourney/createCustomerJourneyCTA/" + data.sessionId + "/" + pm_api.site_token; 422 423 $.ajax({ 424 type: "POST", 425 url: path, 426 data: data, 427 success: function (response) { 428 console.log("Creating CJ status partial-cta-click from plugin"); 429 console.log(response); 430 } 431 }); 432 }, 433 434 saveDrsEvent: function (data) { 435 if(!pm_datalayer_data.allow_pmdrs){ 436 return; 437 } 438 let path = pm_api_customers.path + "customerEvent/save/" + pm_api.site_token; 439 $.ajax({ 440 type: "POST", 441 url: path, 442 data: data, 443 success: function (response) { 444 console.log(response); 445 }, 446 }); 447 }, 448 }; 449 450 document.addEventListener("DOMContentLoaded", function () { 451 if ("DataLayer" in window) { 452 PMDatalayer.parent = DataLayer; 453 DataLayer.register(PMDatalayer); 454 } 455 }); 456 PMDatalayer.storePmOfferId(); 457 458})(jQuery);
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.