1/* Minification failed. Returning unminified contents. 2(2499,27-28): run-time error JS1195: Expected expression: . 3(2499,75-76): run-time error JS1003: Expected ':': , 4(2500,32-33): run-time error JS1009: Expected '}': : 5(2491,33): run-time error JS1004: Expected ';' 6(2500,32-33): run-time error JS1195: Expected expression: : 7(2501,27-28): run-time error JS1195: Expected expression: . 8(2501,79-80): run-time error JS1003: Expected ':': , 9(2502,29): run-time error JS1004: Expected ';' 10(2502,29-30): run-time error JS1195: Expected expression: : 11(2502,37-38): run-time error JS1195: Expected expression: . 12(2502,78-79): run-time error JS1003: Expected ':': , 13(2503,36): run-time error JS1004: Expected ';' 14(2503,36-37): run-time error JS1195: Expected expression: : 15(2504,27-28): run-time error JS1195: Expected expression: . 16(2504,80-81): run-time error JS1003: Expected ':': , 17(2505,25): run-time error JS1004: Expected ';' 18(2505,25-26): run-time error JS1195: Expected expression: : 19(2505,33-34): run-time error JS1195: Expected expression: . 20(2505,48-49): run-time error JS1003: Expected ':': , 21(2507,22): run-time error JS1004: Expected ';' 22(2507,22-23): run-time error JS1195: Expected expression: : 23(2508,13-14): run-time error JS1195: Expected expression: } 24(2508,14-15): run-time error JS1009: Expected '}': ; 25(3094,21-24): run-time error JS1009: Expected '}': ... 26(3096,17-18): run-time error JS1195: Expected expression: } 27(3100,13-26): run-time error JS1006: Expected ')': trackAscEvent 28(3101,9-10): run-time error JS1002: Syntax error: } 29(3101,10-11): run-time error JS1195: Expected expression: , 30(3107,53-54): run-time error JS1010: Expected identifier: ( 31(3152,17-20): run-time error JS1009: Expected '}': ... 32(3153,13-14): run-time error JS1195: Expected expression: } 33(3156,9-10): run-time error JS1002: Syntax error: } 34(3156,10-11): run-time error JS1195: Expected expression: , 35(3162,48-49): run-time error JS1010: Expected identifier: ( 36(3174,17-20): run-time error JS1009: Expected '}': ... 37(3175,13-14): run-time error JS1195: Expected expression: } 38(3200,9-10): run-time error JS1002: Syntax error: } 39(3200,10-11): run-time error JS1195: Expected expression: , 40(3206,39-40): run-time error JS1010: Expected identifier: ( 41(3276,10-11): run-time error JS1195: Expected expression: , 42(3282,35-36): run-time error JS1010: Expected identifier: ( 43(3349,26-27): run-time error JS1195: Expected expression: . 44(3357,30-31): run-time error JS1003: Expected ':': , 45(3358,26-27): run-time error JS1009: Expected '}': : 46(3332,35): run-time error JS1004: Expected ';' 47(3358,26-27): run-time error JS1195: Expected expression: : 48(3363,13-14): run-time error JS1195: Expected expression: } 49(3419,9-10): run-time error JS1002: Syntax error: } 50(3419,10-11): run-time error JS1195: Expected expression: , 51(3425,39-40): run-time error JS1010: Expected identifier: ( 52(3462,17-20): run-time error JS1009: Expected '}': ... 53(3463,13-14): run-time error JS1195: Expected expression: } 54(3475,25-28): run-time error JS1009: Expected '}': ... 55(3475,25-28): run-time error JS1006: Expected ')': ... 56(3474,36): run-time error JS1004: Expected ';' 57(3476,30): run-time error JS1004: Expected ';' 58(3476,30-31): run-time error JS1195: Expected expression: : 59(3477,21-22): run-time error JS1195: Expected expression: } 60(3477,22-23): run-time error JS1195: Expected expression: ) 61(3479,13-14): run-time error JS1002: Syntax error: } 62(3480,9-10): run-time error JS1002: Syntax error: } 63(3480,10-11): run-time error JS1195: Expected expression: , 64(3482,34-35): run-time error JS1010: Expected identifier: ( 65(3501,38-39): run-time error JS1195: Expected expression: . 66(3501,46-47): run-time error JS1195: Expected expression: . 67(3501,55-56): run-time error JS1003: Expected ':': , 68(3507,17-20): run-time error JS1009: Expected '}': ... 69(3508,13-14): run-time error JS1195: Expected expression: } 70(3510,9-10): run-time error JS1002: Syntax error: } 71(3510,10-11): run-time error JS1195: Expected expression: , 72(3516,46-47): run-time error JS1010: Expected identifier: ( 73(3538,17-20): run-time error JS1009: Expected '}': ... 74(3539,13-14): run-time error JS1195: Expected expression: } 75(3550,37-38): run-time error JS1195: Expected expression: . 76(3550,42-43): run-time error JS1003: Expected ':': ; 77(3556,9-10): run-time error JS1002: Syntax error: } 78(3556,10-11): run-time error JS1195: Expected expression: , 79(3562,41-42): run-time error JS1010: Expected identifier: ( 80(3587,17-20): run-time error JS1009: Expected '}': ... 81(3588,13-14): run-time error JS1195: Expected expression: } 82(3631,9-10): run-time error JS1002: Syntax error: } 83(3631,10-11): run-time error JS1195: Expected expression: , 84(3632,5-6): run-time error JS1002: Syntax error: } 85(3652,13-16): run-time error JS1009: Expected '}': ... 86(3654,9-10): run-time error JS1195: Expected expression: } 87(3658,5-6): run-time error JS1002: Syntax error: } 88(3668,28-29): run-time error JS1195: Expected expression: . 89(3668,35-36): run-time error JS1003: Expected ':': ) 90(3669,13-18): run-time error JS1009: Expected '}': const 91(3669,13-18): run-time error JS1006: Expected ')': const 92(3699,5-6): run-time error JS1002: Syntax error: } 93(3847,1-2): run-time error JS1002: Syntax error: } 94(3847,2-3): run-time error JS1195: Expected expression: ) 95(4556,22-23): run-time error JS1195: Expected expression: . 96(4558,35-36): run-time error JS1003: Expected ':': ; 97(9008,73-76): run-time error JS1009: Expected '}': ... 98(9008,73-76): run-time error JS1006: Expected ')': ... 99(9008,72): run-time error JS1004: Expected ';' 100(9008,97): run-time error JS1004: Expected ';' 101(9008,97-98): run-time error JS1195: Expected expression: : 102(9033,82-85): run-time error JS1009: Expected '}': ... 103(9033,82-85): run-time error JS1006: Expected ')': ... 104(9033,81): run-time error JS1004: Expected ';' 105(9033,100): run-time error JS1004: Expected ';' 106(9033,100-101): run-time error JS1195: Expected expression: : 107(9034,5-6): run-time error JS1002: Syntax error: } 108(9034,6-7): run-time error JS1195: Expected expression: ) 109(9045,76-79): run-time error JS1009: Expected '}': ... 110(9045,76-79): run-time error JS1006: Expected ')': ... 111(9045,76): run-time error JS1004: Expected ';' 112(9045,94): run-time error JS1004: Expected ';' 113(9045,94-95): run-time error JS1195: Expected expression: : 114(9046,5-6): run-time error JS1002: Syntax error: } 115(9046,6-7): run-time error JS1195: Expected expression: ) 116(9049,81-84): run-time error JS1009: Expected '}': ... 117(9049,81-84): run-time error JS1006: Expected ')': ... 118(9049,80): run-time error JS1004: Expected ';' 119(9049,99): run-time error JS1004: Expected ';' 120(9049,99-100): run-time error JS1195: Expected expression: : 121(9050,5-6): run-time error JS1002: Syntax error: } 122(9050,6-7): run-time error JS1195: Expected expression: ) 123(9053,81-84): run-time error JS1009: Expected '}': ... 124(9053,81-84): run-time error JS1006: Expected ')': ... 125(9053,80): run-time error JS1004: Expected ';' 126(9053,99): run-time error JS1004: Expected ';' 127(9053,99-100): run-time error JS1195: Expected expression: : 128(9054,5-6): run-time error JS1002: Syntax error: } 129(9054,6-7): run-time error JS1195: Expected expression: ) 130(9057,77-80): run-time error JS1009: Expected '}': ... 131(9057,77-80): run-time error JS1006: Expected ')': ... 132(9057,76): run-time error JS1004: Expected ';' 133(9057,95): run-time error JS1004: Expected ';' 134(9057,95-96): run-time error JS1195: Expected expression: : 135(9058,5-6): run-time error JS1002: Syntax error: } 136(9058,6-7): run-time error JS1195: Expected expression: ) 137(9061,74-77): run-time error JS1009: Expected '}': ... 138(9061,74-77): run-time error JS1006: Expected ')': ... 139(9061,73): run-time error JS1004: Expected ';' 140(9061,92): run-time error JS1004: Expected ';' 141(9061,92-93): run-time error JS1195: Expected expression: : 142(9062,5-6): run-time error JS1002: Syntax error: } 143(9062,6-7): run-time error JS1195: Expected expression: ) 144(9071,74-77): run-time error JS1009: Expected '}': ... 145(9071,74-77): run-time error JS1006: Expected ')': ... 146(9071,73): run-time error JS1004: Expected ';' 147(9071,92): run-time error JS1004: Expected ';' 148(9071,92-93): run-time error JS1195: Expected expression: : 149(9072,5-6): run-time error JS1002: Syntax error: } 150(9072,6-7): run-time error JS1195: Expected expression: ) 151(9075,63-66): run-time error JS1009: Expected '}': ... 152(9075,63-66): run-time error JS1006: Expected ')': ... 153(9075,62): run-time error JS1004: Expected ';' 154(9075,81): run-time error JS1004: Expected ';' 155(9075,81-82): run-time error JS1195: Expected expression: : 156(9076,5-6): run-time error JS1002: Syntax error: } 157(9076,6-7): run-time error JS1195: Expected expression: ) 158(9079,79-82): run-time error JS1009: Expected '}': ... 159(9079,79-82): run-time error JS1006: Expected ')': ... 160(9079,78): run-time error JS1004: Expected ';' 161(9079,97): run-time error JS1004: Expected ';' 162(9079,97-98): run-time error JS1195: Expected expression: : 163(9080,5-6): run-time error JS1002: Syntax error: } 164(9080,6-7): run-time error JS1195: Expected expression: ) 165(9083,69-72): run-time error JS1009: Expected '}': ... 166(9083,69-72): run-time error JS1006: Expected ')': ... 167(9083,68): run-time error JS1004: Expected ';' 168(9083,87): run-time error JS1004: Expected ';' 169(9083,87-88): run-time error JS1195: Expected expression: : 170(9084,5-6): run-time error JS1002: Syntax error: } 171(9084,6-7): run-time error JS1195: Expected expression: ) 172(9087,74-77): run-time error JS1009: Expected '}': ... 173(9087,74-77): run-time error JS1006: Expected ')': ... 174(9087,73): run-time error JS1004: Expected ';' 175(9087,92): run-time error JS1004: Expected ';' 176(9087,92-93): run-time error JS1195: Expected expression: : 177(9088,5-6): run-time error JS1002: Syntax error: } 178(9088,6-7): run-time error JS1195: Expected expression: ) 179(9091,76-79): run-time error JS1009: Expected '}': ... 180(9091,76-79): run-time error JS1006: Expected ')': ... 181(9091,75): run-time error JS1004: Expected ';' 182(9091,94): run-time error JS1004: Expected ';' 183(9091,94-95): run-time error JS1195: Expected expression: : 184(9092,5-6): run-time error JS1002: Syntax error: } 185(9092,6-7): run-time error JS1195: Expected expression: ) 186(9095,69-72): run-time error JS1009: Expected '}': ... 187(9095,69-72): run-time error JS1006: Expected ')': ... 188(9095,68): run-time error JS1004: Expected ';' 189(9095,87): run-time error JS1004: Expected ';' 190(9095,87-88): run-time error JS1195: Expected expression: : 191(9096,5-6): run-time error JS1002: Syntax error: } 192(9096,6-7): run-time error JS1195: Expected expression: )
193(9099,71-74): run-time error JS1009: Expected '}': ... 194(9099,71-74): run-time error JS1006: Expected ')': ... 195(9099,70): run-time error JS1004: Expected ';' 196(9099,89): run-time error JS1004: Expected ';' 197(9099,89-90): run-time error JS1195: Expected expression: : 198(9100,5-6): run-time error JS1002: Syntax error: } 199(9100,6-7): run-time error JS1195: Expected expression: ) 200(9103,71-74): run-time error JS1009: Expected '}': ... 201(9103,71-74): run-time error JS1006: Expected ')': ... 202(9103,70): run-time error JS1004: Expected ';' 203(9103,89): run-time error JS1004: Expected ';' 204(9103,89-90): run-time error JS1195: Expected expression: : 205(9104,5-6): run-time error JS1002: Syntax error: } 206(9104,6-7): run-time error JS1195: Expected expression: ) 207(9107,78-81): run-time error JS1009: Expected '}': ... 208(9107,78-81): run-time error JS1006: Expected ')': ... 209(9107,77): run-time error JS1004: Expected ';' 210(9107,96): run-time error JS1004: Expected ';' 211(9107,96-97): run-time error JS1195: Expected expression: : 212(9108,5-6): run-time error JS1002: Syntax error: } 213(9108,6-7): run-time error JS1195: Expected expression: ) 214(9111,89-92): run-time error JS1009: Expected '}': ... 215(9111,89-92): run-time error JS1006: Expected ')': ... 216(9111,88): run-time error JS1004: Expected ';' 217(9111,107): run-time error JS1004: Expected ';' 218(9111,107-108): run-time error JS1195: Expected expression: : 219(9112,5-6): run-time error JS1002: Syntax error: } 220(9112,6-7): run-time error JS1195: Expected expression: ) 221(9119,70-73): run-time error JS1009: Expected '}': ... 222(9119,70-73): run-time error JS1006: Expected ')': ... 223(9119,69): run-time error JS1004: Expected ';' 224(9119,88): run-time error JS1004: Expected ';' 225(9119,88-89): run-time error JS1195: Expected expression: : 226(9120,5-6): run-time error JS1002: Syntax error: } 227(9120,6-7): run-time error JS1195: Expected expression: ) 228(9123,70-73): run-time error JS1009: Expected '}': ... 229(9123,70-73): run-time error JS1006: Expected ')': ... 230(9123,69): run-time error JS1004: Expected ';' 231(9123,88): run-time error JS1004: Expected ';' 232(9123,88-89): run-time error JS1195: Expected expression: : 233(9124,5-6): run-time error JS1002: Syntax error: } 234(9124,6-7): run-time error JS1195: Expected expression: ) 235(9128,77-80): run-time error JS1009: Expected '}': ... 236(9128,77-80): run-time error JS1006: Expected ')': ... 237(9128,76): run-time error JS1004: Expected ';' 238(9128,95): run-time error JS1004: Expected ';' 239(9128,95-96): run-time error JS1195: Expected expression: : 240(9129,5-6): run-time error JS1002: Syntax error: }
241(9129,6-7): run-time error JS1195: Expected expression: ) 242(9132,77-80): run-time error JS1009: Expected '}': ... 243(9132,77-80): run-time error JS1006: Expected ')': ... 244(9132,76): run-time error JS1004: Expected ';' 245(9132,95): run-time error JS1004: Expected ';' 246(9132,95-96): run-time error JS1195: Expected expression: : 247(9133,5-6): run-time error JS1002: Syntax error: } 248(9133,6-7): run-time error JS1195: Expected expression: ) 249(9136,77-80): run-time error JS1009: Expected '}': ... 250(9136,77-80): run-time error JS1006: Expected ')': ... 251(9136,76): run-time error JS1004: Expected ';' 252(9136,95): run-time error JS1004: Expected ';' 253(9136,95-96): run-time error JS1195: Expected expression: : 254(9137,5-6): run-time error JS1002: Syntax error: } 255(9137,6-7): run-time error JS1195: Expected expression: ) 256(9140,80-83): run-time error JS1009: Expected '}': ... 257(9140,80-83): run-time error JS1006: Expected ')': ... 258(9140,79): run-time error JS1004: Expected ';' 259(9140,98): run-time error JS1004: Expected ';' 260(9140,98-99): run-time error JS1195: Expected expression: : 261(9141,5-6): run-time error JS1002: Syntax error: } 262(9141,6-7): run-time error JS1195: Expected expression: ) 263(9145,80-83): run-time error JS1009: Expected '}': ... 264(9145,80-83): run-time error JS1006: Expected ')': ... 265(9145,79): run-time error JS1004: Expected ';' 266(9145,98): run-time error JS1004: Expected ';' 267(9145,98-99): run-time error JS1195: Expected expression: : 268(9146,5-6): run-time error JS1002: Syntax error: } 269(9146,6-7): run-time error JS1195: Expected expression: ) 270(9149,80-83): run-time error JS1009: Expected '}': ... 271(9149,80-83): run-time error JS1006: Expected ')': ... 272(9149,79): run-time error JS1004: Expected ';' 273(9149,98): run-time error JS1004: Expected ';' 274(9149,98-99): run-time error JS1195: Expected expression: : 275(9150,5-6): run-time error JS1002: Syntax error: } 276(9150,6-7): run-time error JS1195: Expected expression: ) 277(9153,80-83): run-time error JS1009: Expected '}': ... 278(9153,80-83): run-time error JS1006: Expected ')': ... 279(9153,79): run-time error JS1004: Expected ';' 280(9153,98): run-time error JS1004: Expected ';' 281(9153,98-99): run-time error JS1195: Expected expression: : 282(9154,5-6): run-time error JS1002: Syntax error: } 283(9154,6-7): run-time error JS1195: Expected expression: ) 284(9157,80-83): run-time error JS1009: Expected '}': ... 285(9157,80-83): run-time error JS1006: Expected ')': ... 286(9157,79): run-time error JS1004: Expected ';' 287(9157,98): run-time error JS1004: Expected ';' 288(9157,98-99): run-time error JS1195: Expected expression: : 289(9158,5-6): run-time error JS1002: Syntax error: } 290(9158,6-7): run-time error JS1195: Expected expression: ) 291(9161,80-83): run-time error JS1009: Expected '}': ... 292(9161,80-83): run-time error JS1006: Expected ')': ... 293(9161,79): run-time error JS1004: Expected ';' 294(9161,98): run-time error JS1004: Expected ';' 295(9161,98-99): run-time error JS1195: Expected expression: : 296(9162,5-6): run-time error JS1002: Syntax error: } 297(9162,6-7): run-time error JS1195: Expected expression: ) 298(9165,80-83): run-time error JS1009: Expected '}': ... 299(9165,80-83): run-time error JS1006: Expected ')': ... 300(9165,79): run-time error JS1004: Expected ';' 301(9165,98): run-time error JS1004: Expected ';' 302(9165,98-99): run-time error JS1195: Expected expression: : 303(9166,5-6): run-time error JS1002: Syntax error: } 304(9166,6-7): run-time error JS1195: Expected expression: ) 305(9169,80-83): run-time error JS1009: Expected '}': ... 306(9169,80-83): run-time error JS1006: Expected ')': ... 307(9169,79): run-time error JS1004: Expected ';' 308(9169,98): run-time error JS1004: Expected ';' 309(9169,98-99): run-time error JS1195: Expected expression: : 310(9170,5-6): run-time error JS1002: Syntax error: } 311(9170,6-7): run-time error JS1195: Expected expression: ) 312(9173,80-83): run-time error JS1009: Expected '}': ... 313(9173,80-83): run-time error JS1006: Expected ')': ... 314(9173,79): run-time error JS1004: Expected ';' 315(9173,98): run-time error JS1004: Expected ';' 316(9173,98-99): run-time error JS1195: Expected expression: : 317(9174,5-6): run-time error JS1002: Syntax error: } 318(9174,6-7): run-time error JS1195: Expected expression: ) 319(9177,80-83): run-time error JS1009: Expected '}': ... 320(9177,80-83): run-time error JS1006: Expected ')': ... 321(9177,79): run-time error JS1004: Expected ';' 322(9177,98): run-time error JS1004: Expected ';' 323(9177,98-99): run-time error JS1195: Expected expression: : 324(9178,5-6): run-time error JS1002: Syntax error: } 325(9178,6-7): run-time error JS1195: Expected expression: ) 326(9181,80-83): run-time error JS1009: Expected '}': ... 327(9181,80-83): run-time error JS1006: Expected ')': ... 328(9181,79): run-time error JS1004: Expected ';' 329(9181,98): run-time error JS1004: Expected ';' 330(9181,98-99): run-time error JS1195: Expected expression: : 331(9182,5-6): run-time error JS1002: Syntax error: } 332(9182,6-7): run-time error JS1195: Expected expression: ) 333(9186,79-82): run-time error JS1009: Expected '}': ... 334(9186,79-82): run-time error JS1006: Expected ')': ... 335(9186,78): run-time error JS1004: Expected ';' 336(9186,97): run-time error JS1004: Expected ';' 337(9186,97-98): run-time error JS1195: Expected expression: : 338(9187,5-6): run-time error JS1002: Syntax error: } 339(9187,6-7): run-time error JS1195: Expected expression: ) 340(9189,79-82): run-time error JS1009: Expected '}': ... 341(9189,79-82): run-time error JS1006: Expected ')': ... 342(9189,78): run-time error JS1004: Expected ';' 343(9189,97): run-time error JS1004: Expected ';' 344(9189,97-98): run-time error JS1195: Expected expression: : 345(9190,5-6): run-time error JS1002: Syntax error: } 346(9190,6-7): run-time error JS1195: Expected expression: ) 347(9193,79-82): run-time error JS1009: Expected '}': ... 348(9193,79-82): run-time error JS1006: Expected ')': ... 349(9193,78): run-time error JS1004: Expected ';' 350(9193,97): run-time error JS1004: Expected ';' 351(9193,97-98): run-time error JS1195: Expected expression: : 352(9194,5-6): run-time error JS1002: Syntax error: } 353(9194,6-7): run-time error JS1195: Expected expression: ) 354(9197,79-82): run-time error JS1009: Expected '}': ... 355(9197,79-82): run-time error JS1006: Expected ')': ... 356(9197,78): run-time error JS1004: Expected ';' 357(9197,97): run-time error JS1004: Expected ';' 358(9197,97-98): run-time error JS1195: Expected expression: : 359(9198,5-6): run-time error JS1002: Syntax error: } 360(9198,6-7): run-time error JS1195: Expected expression: ) 361(9201,79-82): run-time error JS1009: Expected '}': ... 362(9201,79-82): run-time error JS1006: Expected ')': ... 363(9201,78): run-time error JS1004: Expected ';' 364(9201,97): run-time error JS1004: Expected ';' 365(9201,97-98): run-time error JS1195: Expected expression: : 366(9202,5-6): run-time error JS1002: Syntax error: } 367(9202,6-7): run-time error JS1195: Expected expression: ) 368(9205,79-82): run-time error JS1009: Expected '}': ... 369(9205,79-82): run-time error JS1006: Expected ')': ... 370(9205,78): run-time error JS1004: Expected ';' 371(9205,97): run-time error JS1004: Expected ';' 372(9205,97-98): run-time error JS1195: Expected expression: : 373(9206,5-6): run-time error JS1002: Syntax error: } 374(9206,6-7): run-time error JS1195: Expected expression: ) 375(9210,74-77): run-time error JS1009: Expected '}': ... 376(9210,74-77): run-time error JS1006: Expected ')': ... 377(9210,73): run-time error JS1004: Expected ';' 378(9210,92): run-time error JS1004: Expected ';' 379(9210,92-93): run-time error JS1195: Expected expression: : 380(9211,5-6): run-time error JS1002: Syntax error: } 381(9211,6-7): run-time error JS1195: Expected expression: ) 382(9214,74-77): run-time error JS1009: Expected '}': ... 383(9214,74-77): run-time error JS1006: Expected ')': ... 384(9214,73): run-time error JS1004: Expected ';' 385(9214,92): run-time error JS1004: Expected ';' 386(9214,92-93): run-time error JS1195: Expected expression: : 387(9215,5-6): run-time error JS1002: Syntax error: } 388(9215,6-7): run-time error JS1195: Expected expression: ) 389(9218,74-77): run-time error JS1009: Expected '}': ... 390(9218,74-77): run-time error JS1006: Expected ')': ... 391(9218,73): run-time error JS1004: Expected ';' 392(9218,92): run-time error JS1004: Expected ';' 393(9218,92-93): run-time error JS1195: Expected expression: : 394(9219,5-6): run-time error JS1002: Syntax error: } 395(9219,6-7): run-time error JS1195: Expected expression: ) 396(9222,74-77): run-time error JS1009: Expected '}': ... 397(9222,74-77): run-time error JS1006: Expected ')': ... 398(9222,73): run-time error JS1004: Expected ';' 399(9222,92): run-time error JS1004: Expected ';' 400(9222,92-93): run-time error JS1195: Expected expression: : 401(9223,5-6): run-time error JS1002: Syntax error: } 402(9223,6-7): run-time error JS1195: Expected expression: ) 403(9226,74-77): run-time error JS1009: Expected '}': ... 404(9226,74-77): run-time error JS1006: Expected ')': ... 405(9226,73): run-time error JS1004: Expected ';' 406(9226,92): run-time error JS1004: Expected ';' 407(9226,92-93): run-time error JS1195: Expected expression: : 408(9227,5-6): run-time error JS1002: Syntax error: } 409(9227,6-7): run-time error JS1195: Expected expression: ) 410(9230,74-77): run-time error JS1009: Expected '}': ... 411(9230,74-77): run-time error JS1006: Expected ')': ... 412(9230,73): run-time error JS1004: Expected ';' 413(9230,92): run-time error JS1004: Expected ';' 414(9230,92-93): run-time error JS1195: Expected expression: : 415(9231,5-6): run-time error JS1002: Syntax error: } 416(9231,6-7): run-time error JS1195: Expected expression: ) 417(9234,80-83): run-time error JS1009: Expected '}': ... 418(9234,80-83): run-time error JS1006: Expected ')': ... 419(9234,79): run-time error JS1004: Expected ';' 420(9235,5-6): run-time error JS1002: Syntax error: } 421(9235,6-7): run-time error JS1195: Expected expression: ) 422(9238,81-84): run-time error JS1009: Expected '}': ... 423(9238,81-84): run-time error JS1006: Expected ')': ... 424(9238,80): run-time error JS1004: Expected ';' 425(9239,5-6): run-time error JS1002: Syntax error: }
426(9239,6-7): run-time error JS1195: Expected expression: ) 427(9242,80-83): run-time error JS1009: Expected '}': ... 428(9242,80-83): run-time error JS1006: Expected ')': ... 429(9242,79): run-time error JS1004: Expected ';' 430(9243,5-6): run-time error JS1002: Syntax error: } 431(9243,6-7): run-time error JS1195: Expected expression: ) 432(9246,84-87): run-time error JS1009: Expected '}': ... 433(9246,84-87): run-time error JS1006: Expected ')': ... 434(9246,83): run-time error JS1004: Expected ';' 435(9247,5-6): run-time error JS1002: Syntax error: } 436(9247,6-7): run-time error JS1195: Expected expression: ) 437(9335,1-2): run-time error JS1002: Syntax error: } 438(9335,2-3): run-time error JS1195: Expected expression: ) 439(3412,21-27): run-time error JS1018: 'return' statement outside of function: return 440(3408,21-27): run-time error JS1018: 'return' statement outside of function: return 441(3394,21-27): run-time error JS1018: 'return' statement outside of function: return 442(3387,21-27): run-time error JS1018: 'return' statement outside of function: return 443 */ 444/* eslint-disable */ 445// Array.find 446// https://tc39.github.io/ecma262/#sec-array.prototype.find 447if (!Array.prototype.find) { 448 Object.defineProperty(Array.prototype, "find", { 449 value: function (predicate) { 450 // 1. Let O be ? ToObject(this value). 451 if (this == null) { 452 throw new TypeError('"this" is null or not defined'); 453 } 454 455 var o = Object(this); 456 457 // 2. Let len be ? ToLength(? Get(O, "length")). 458 var len = o.length >>> 0; 459 460 // 3. If IsCallable(predicate) is false, throw a TypeError exception. 461 if (typeof predicate !== "function") { 462 throw new TypeError("predicate must be a function"); 463 } 464 465 // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. 466 var thisArg = arguments[1]; 467 468 // 5. Let k be 0. 469 var k = 0; 470 471 // 6. Repeat, while k < len 472 while (k < len) { 473 // a. Let Pk be ! ToString(k). 474 // b. Let kValue be ? Get(O, Pk). 475 // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). 476 // d. If testResult is true, return kValue. 477 var kValue = o[k]; 478 if (predicate.call(thisArg, kValue, k, o)) { 479 return kValue; 480 } 481 // e. Increase k by 1. 482 k++; 483 } 484 485 // 7. Return undefined. 486 return undefined; 487 }, 488 configurable: true, 489 writable: true 490 }); 491} 492 493// Object.assign 494// added to decouple us from $.extend 495if (typeof Object.assign !== "function") { 496 // Must be writable: true, enumerable: false, configurable: true 497 Object.defineProperty(Object, "assign", { 498 value: function assign(target, varArgs) { // .length of function is 2 499 "use strict"; 500 if (target == null) { // TypeError if undefined or null 501 throw new TypeError("Cannot convert undefined or null to object"); 502 } 503 504 var to = Object(target); 505 506 for (var index = 1; index < arguments.length; index++) { 507 var nextSource = arguments[index]; 508 509 if (nextSource != null) { // Skip over if undefined or null 510 for (var nextKey in nextSource) { 511 // Avoid bugs when hasOwnProperty is shadowed 512 if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 513 to[nextKey] = nextSource[nextKey]; 514 } 515 } 516 } 517 } 518 return to; 519 }, 520 writable: true, 521 configurable: true 522 }); 523} 524 525 526 527// Object.entries 528// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#Polyfill 529// dependent on Object.keys (we don't support IE9 530// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys 531if (!Object.entries) { 532 Object.entries = function (obj) { 533 var ownProps = Object.keys(obj), 534 i = ownProps.length, 535 resArray = new Array(i); // preallocate the Array 536 while (i--) 537 resArray[i] = [ownProps[i], obj[ownProps[i]]]; 538 539 return resArray; 540 }; 541} 542 543if (!String.prototype.endsWith) { 544 String.prototype.endsWith = function (search, this_len) { 545 if (this_len === undefined || this_len > this.length) { 546 this_len = this.length; 547 } 548 return this.substring(this_len - search.length, this_len) === search; 549 }; 550} 551 552if (!Array.prototype.includes) { 553 Array.prototype.includes = function (term) { 554 return (this.indexOf(term) !== -1); 555 }; 556} 557 558 559/* eslint-enable */; 560(function (wts, modules) { 561 "use strict"; 562 563 //
564 // utilities that help keep our code cleaner 565 // 566 567 var _ = (modules.utilities = { 568 FORBIDDEN_SYMBOLS: /[[\]*{}^<>!?\\;=|~]+/g, 569 570 /** 571 * Returns the input value â useful as a functional predicate 572 * @param {any} value the data passed to this fn, if any 573 * @returns {any} the argument value 574 */ 575 identity: function (value) { 576 return value; 577 }, 578 579 // returns true|false 580 isNullOrUndefined: function (input) { 581 return input === null || typeof input === "undefined"; 582 }, 583 584 // returns true|false 585 isNumber: function (input) { 586 return $.isNumeric(input); 587 }, 588 589 // returns true|false 590 isInteger: function (input) { 591 return _.isNumber(input) && input === Math.round(input); 592 }, 593 594 /** 595 * @description Binds all functions of an object (and it's prototype) to the context of the object. 596 * @param {object}
596 obj - an object containing function members 597 * @param {object} [scope=undefined] - scope in which the target members will be bound 598 * @param {string[]} [exceptions=undefined] - a string list of function names that should not be bound 599 * @returns {undefined} 600 */ 601 bindAll: function (obj, scope, exceptions) { 602 var scopeParam = typeof scope === "undefined" ? obj : scope; 603 var exceptionsParam = exceptions || []; 604 605 var keys = []; 606 for (var key in obj) keys.push(key); // eslint-disable-line 607 608 keys.filter(function (k) { 609 return exceptionsParam.indexOf(k) < 0; 610 }).forEach(function (k) { 611 if (typeof obj[k] === "function") 612 obj[k] = obj[k].bind(scopeParam); 613 }); 614 }, 615 616 /** 617 * @description Returns a function, that, as long as it continues to be invoked, will not be triggered. 618 * The function will be called after it stops being called for N milliseconds. If `immediate` is passed, trigger the 619 * function on the leading edge, instead of the trailing. 620 * @param {Function} func - function to debounce 621 * @param {number} wait time that must pass without an additional call, in milliseconds 622 * @param {boolean} [immediate=undefined] - immediate Whether the callback should be called on the leading edge of debouncing 623 * @see https://davidwalsh.name/javascript-debounce-function 624 * @returns {CallableFunction} returns the debounced function 625 */ 626 debounce: function (func, wait, immediate) { 627 var timeout = null; 628 function run() { 629 var context = this; 630 var args = arguments; 631 632 var later = function () { 633 timeout = null; 634 if (!immediate) { 635 func.apply(context, args); 636 } 637 }; 638 639 var callNow = immediate && !timeout; 640 clearTimeout(timeout); 641 timeout = setTimeout(later, wait); 642 if (callNow) { 643 func.apply(context, args); 644 } 645 } 646 647 run.cancel = function () { 648 if (timeout !== null) { 649 clearTimeout(timeout); 650 timeout = null; 651 } 652 }; 653 654 return run; 655 }, 656 657 /** 658 * Scroll the viewport to a specific element 659 * @param {jQuery|HTMLElement} element The element to scroll to 660 * @param {int} duration A duration to scroll, in milliseconds 661 * @returns {undefined} 662 */ 663 scrollToElement: function (element, duration) { 664 var $element = null; 665 666 if (!($element instanceof window.jQuery)) { 667 $element = $(element); 668 } else { 669 $element = element; 670 } 671 672 $("html, body").animate( 673 { 674 scrollTop: $element.offset().top, 675 }, 676 duration 677 ); 678 }, 679 680 /** 681 * Defers a function to be called at the "next tick". 682 * @param {Function} fn A function that can be called 683 * @returns {int} An ID for calling window.cancelTimeout 684 * @todo Update this to return a deferred function, instead of immediately deferring 685 */ 686 defer: function (fn) { 687 return window.setTimeout(fn, 0); 688 }, 689 690 /** 691 * Decodes HTML via jQuery 692 * @param {string} text Text to be decoded 693 * @returns {string} the decoded text 694 * @see http://stackoverflow.com/questions/1219860/html-encoding-in-javascript-jquery 695 */ 696 encodeHTML: function (text) { 697 if (text === null) { 698 return null; 699 } 700 701 return text 702 .replace(/"/g, """) 703 .replace(/'/g, "'") 704 .replace(/</g, "<") 705 .replace(/>/g, ">"); 706 }, 707 708 /** 709 * Decodes HTML via jQuery 710 * @param {string} text Text to be decoded 711 * @returns {string} the decoded text 712 */ 713 decodeHTML: function (text) { 714 return $("<div />").html(text).text(); 715 }, 716 717 /** 718 * Escapes HTML via jQuery 719 * @param {string} text Text to be escaped 720 * @returns {string} Text that is escaped 721 */ 722 escapeHTML: function (text) { 723 return $("<div/>").text(text).html(); 724 }, 725 726 /** 727 * Retrieves a truck's ID from the DOM, likely for use on VSP 728 * @param {HTMLElement} element An element representing the .truck component or one if its children 729 * @returns {string|null} The truck ID, or null 730 */ 731 getTruckID: function (element) { 732 var $truck = $(element).closest(".truck"); 733 return $truck.data("truck-id") || null; 734 }, 735 736 /** 737 * Retrieves a query string parameter by name 738 * @todo This methodology doesn't properly accomodate for multiple items per name 739 * @param {string} name The key to look for in the search 740 * @returns {string|null} The query parameter, as a string or null 741 */ 742 getQueryParameter: function (name) { 743 var search = window.location.search.substring(1); 744 if (search.length === 0) { 745 return null; 746 } 747 748 var parts = search.split("&"); 749 var queryStringParams = parts.reduce(function (map, part, i) { 750 var keyValue = part[i].split("="); 751 var key = keyValue[0]; 752 var value = keyValue[1]; 753 754 map[key] = value; 755 756 return map; 757 }, {}); 758 759 var hasValue = typeof queryStringParams[name] === undefined; 760 return hasValue ? null : queryStringParams[name]; 761 }, 762 763 /** 764 * Convenience method for building a selector string for matching a name attribute 765 * General usage is to then pass the result to a jQuery selector 766 * @remarks This might be better as a jQuery plugin 767 * @param {string} [parent=undefined] A selector string representing a parent of a target element `.parent [name="name"]` 768 * @param {string} [selector=undefined] A selector string for prepending to the name selector `.parent .selector[name="name"]` 769 * @param {string} name The name of the element 770 * @returns {string} The selector string 771 */ 772 nameSelector: function (/* [parent, selector,] name */) { 773 var parent = ""; 774 var selector = ""; 775 var name = ""; 776 777 switch (arguments.length) { 778 case 1: 779 name = arguments[0]; 780 break; 781 782 case 2: 783 selector = arguments[0]; 784 name = arguments[1]; 785 break; 786 787 case 3: 788 parent = arguments[0]; 789 selector = arguments[1]; 790 name = arguments[2]; 791 break; 792 } 793 794 return parent + " " + selector + "[name='" + name + "']"; 795 }, 796 797 /** 798 * Retrieves the truck title text and photo from a vehicle listing 799 * Previously named "GetTruckListingTitleAndPhoto" in _InventoryLayout.cshtml 800 * @param {jQuery} $listing The truck listing in which a truck title and photo are being retrieved from 801 * @returns {any}
801 An object containing "title" and "image" fields 802 * representing title text and img src of the vehicle 803 */ 804 getTruckTitleAndPhoto: function ($listing) { 805 var $image = $listing.find(".truck-image__image"); 806 807 // CASE: because of lazy-loading library which uses data-src, the attribute "src" may 808 // not be set on page load, so we fallback to the src property (as assigned by lazy-load) 809 // or the data-src attribute, which lazy-load would load. 810 var imageSrc = 811 $image.attr("src") || 812 $image.prop("src") || 813 $image.attr("data-src"); 814 815 return { 816 title: $listing.find(".truck__link-text").text().trim(), 817 image: imageSrc, 818 truckID: $listing.data("truckId"), 819 }; 820 }, 821 822 /** 823 * Parse a phone string for display: (530) 123-4567, Ext. 890 824 * (this frontend method mirrors the logic of the WTSUtils.StringUtils.ParseForPhoneDisplay backend method) 825 * @param {string} phoneNo The phone no. 826 * @returns {string} A formatted phone number 827 */ 828 parseForPhoneDisplay: function (phoneNo) { 829 if (!phoneNo) { 830 return ""; 831 } 832 833 let parts = this.parsePhoneString(phoneNo); 834 let telPhoneCode = ""; 835 836 if (parts.isValid) { 837 if (parts.extension) { 838 telPhoneCode = `(${parts.area}) ${parts.prefix}-${parts.suffix}, Ext. ${parts.extension}`; 839 } else { 840 telPhoneCode = `(${parts.area}) ${parts.prefix}-${parts.suffix}`; 841 } 842 } 843 844 return telPhoneCode; 845 }, 846 847 /** 848 * Parse the phone string into its parts. 849 * @param {string} phoneNo The phone no. 850 * @returns {PhoneParts} A JavaScript object containing the parsed area code, 851 * prefix, suffix, and extension of the provided phone number 852 */ 853 parsePhoneString: function (phoneNo) { 854 /** 855 * @class PhoneParts 856 * @type {Object} 857 * @property {boolean} isValid 858 * @property {string} area The phone number area code. 859 * @property {string} prefix The phone number prefix. 860 * @property {string} suffix The phone number suffix. 861 * @property {string} extension The phone number extension. 862 */ 863 class PhoneParts { 864 constructor(isValid, area, prefix, suffix, extension) { 865 this.isValid = isValid; 866 this.area = area; 867 this.prefix = prefix; 868 this.suffix = suffix; 869 this.extension = extension; 870 } 871 } 872 873 const phoneDigitsRegex = new RegExp("[^0-9\\x]"); 874 875 if (!phoneNo) { 876 return new PhoneParts(false, "", "", "", ""); 877 } 878 879 // Remove everything but numerals and the x 880 let cleanString = phoneNo.replace(phoneDigitsRegex, ""); 881 882 // If phoneNo started with 1 or 0 then 'x' will end up in the wrong position, 883 // so we'll just remove it and parse the remaining digits. 884 // 7-digit numbers are a thing of the past. Phone field validation rejects anything 885 // less than 10 digits, so the position of the 'x' is not important and could be problematic 886 cleanString = cleanString.replace("x", ""); 887 888 // There are no U.S. area codes (or prefixes) that start with 1 (or 0), so let's get rid of those leading digits. 889 if (cleanString.startsWith("0")) { 890 // We really can't guess what the number should be, so just bail out 891 return new PhoneParts(false, "", "", "", ""); 892 } 893 894 while (cleanString.startsWith("1")) { 895 // Here a leading 1 is probably just a bad habit taught by thousands of companies that think their users 896 // don't know how to use a phone ... in an era when most calls are mobile and don't need to start with 1. 897 cleanString = cleanString.substring(1); 898 } 899 900 // Now let's get the extension 901 let extension = null; 902 903 if (cleanString.length > 10) { 904 // get the extension 905 extension = cleanString.substring(10); 906 907 // remove the extension from original string 908 cleanString = cleanString.substring(0, 10); 909 } 910 911 if (cleanString.length == 10) { 912 let area = cleanString.substring(0, 3); 913 let prefix = cleanString.substring(3, 6); 914 let suffix = cleanString.substring(6, 10); 915 916 return new PhoneParts(true, area, prefix, suffix, extension); 917 } 918
919 // Anything else is not valid 920 return new PhoneParts(false, "", "", "", ""); 921 }, 922 923 /** 924 * Detect if a containing HTMLElement has content overflow on either axis 925 * @param {jQuery|HTMLElement} element The element whose content overflow status you want to check 926 * @returns {boolean} 927 */ 928 hasContentOverflow: function (element) { 929 var $element = null; 930 931 if (!($element instanceof window.jQuery)) { 932 $element = $(element); 933 } else { 934 $element = element; 935 } 936 937 return ( 938 $element.prop("scrollWidth") > $element.width() || 939 $element.prop("scrollHeight") > $element.height() 940 ); 941 }, 942 943 hasForbiddenSymbols: function (str) { 944 return str.match(_.FORBIDDEN_SYMBOLS) !== null; 945 }, 946 947 /** 948 * Helper function used to get the text of the innermost html element. Helpful when the text of "what" was clicked is needed. 949 * @param {any} $element the element to get the innermost text from. 950 * @returns {string} The inner text of the element. 951 */ 952 getInnerText: function ($el) { 953 var $innermostChild = $el; 954 var $next = $innermostChild; 955 956 do { 957 $innermostChild = $next; 958 $next = $next.children(); 959 } while ($next.length); 960 961 return $innermostChild.html().trim(); 962 }, 963 964 /** 965 * Full visible text of an element including nested children (e.g. anchor with styled spans). 966 * @param {any} $el the element to get aggregate text from. 967 * @returns {string} The trimmed text content of the element. 968 */ 969 getAggregateText: function ($el) { 970 return ($el.text() || "").trim(); 971 }, 972 973 /** 974 * Helper function used to get the appropriate carousel description 975 * @param {any} $el The item within the carousel to resolve a desciption for. 976 * @returns Either "similar-vehicle-carousel" OR "recently-viewed-carousel", depending on which carousel was resolved from $el. 977 */ 978 getCarouselDescription: function ($el) { 979 var $carousel = $el.parents( 980 ".wts-vehicle-carousel--similar-vehicles-carousel" 981 ); 982 983 if ($carousel.length) { 984 return "similar-vehicle-carousel"; 985 } else { 986 var $carousel = $el.parents( 987 ".wts-vehicle-carousel--recently-viewed-carousel" 988 ); 989 if (!$carousel.length) { 990 // parent carousel not found so return nothing 991 return; 992 } 993 994 return "recently-viewed-carousel"; 995 } 996 }, 997 998 /** 999 * Detects the device type by observing different window.matchMedia queries to test the screen's width. 1000 * Uses responsive breakpoints: 90rem (pc/desktop), 40rem (tablet), below that is mobile. 1001 * @returns {"pc"|"tablet"|"mobile"} The device type based on screen width. 1002 */ 1003 detectDeviceType: function () { 1004 if (window.matchMedia("(min-width: 90rem)").matches) { 1005 return "pc"; 1006 } else if (window.matchMedia("(min-width: 40rem)").matches) { 1007 return "tablet"; 1008 } else { 1009 return "mobile"; 1010 } 1011 }, 1012 1013 /** 1014 * Normalizes dial strings to E.164 for ASC comm_phone_number (US-centric +1XXXXXXXXXX). 1015 * @param {string} input tel href fragment, display text, or digits 1016 * @returns {string} E.164 or empty if not enough digits 1017 */ 1018 phoneToE164: function (input) { 1019 if (input == null || input === "") { 1020 return ""; 1021 } 1022 var digits = String(input) 1023 .replace(/^tel:/i, "") 1024 .replace(/\D/g, ""); 1025 if (digits.length === 10) { 1026 return "+1" + digits; 1027 } 1028 if (digits.length === 11 && digits.charAt(0) === "1") { 1029 return "+" + digits; 1030 } 1031 if (digits.length >= 10) { 1032 return "+" + digits; 1033 } 1034 return ""; 1035 }, 1036 });
1037})(window.wts, window.wts.modules); 1038; 1039; (function (wts, modules) { 1040 1041 // this is the correct mime type we expect for these 1042 var utilities = modules.utilities; 1043 var mimeType = "application/json"; 1044 var cache = Object.create(null); 1045 1046 /** 1047 * Finds and parses the JSON script tag associated to the selector parameter, or returns null 1048 * @param {string} selector The DOMString corrosponding to the id of the <script> tag to find 1049 * @throws {DOMException} Upon an invalid selector 1050 * @throws {Error} Upon reading a script with an incorrect mime-type â should be "application/json" 1051 * @returns {any} The result of parsing the script tag 1052 */ 1053 modules.getClientData = function (selector) { 1054 1055 // repeated calls don't reparse the JSON 1056 if (!utilities.isNullOrUndefined(cache[selector])) { 1057 return cache[selector]; 1058 } 1059 1060 var scriptEl = document.querySelector(selector); 1061 1062 // enforce us to not incorrectly attaching running-code 1063 if (scriptEl !== null && scriptEl.type.toLowerCase() !== mimeType) { 1064 throw new Error("Script corresponding to '" + selector + "' is not of type '" + mimeType + "'"); 1065 } 1066 1067 if (scriptEl === null) { 1068 return null; 1069 } 1070 1071 var data = null; 1072 1073 try { 1074 data = JSON.parse(scriptEl.innerText); 1075 } 1076 catch (ex) { 1077 console.error(ex); 1078 } 1079 1080 cache[selector] = data; 1081 1082 return data; 1083 }; 1084 1085 1086})(window.wts, window.wts.modules);; 1087/** 1088 * @module EventEmitter 1089 */ 1090; (function (wts, modules) { 1091 "use strict"; 1092 1093 var WILDCARD_CHAR = "*"; 1094 1095 /** 1096 * @description A custom, cross-browser compliant event-emitter. Useful for implementing modules with observer-like 1097 * patterns (listeners). Not meant to be used for DOM-related events, meant for higher-level events. 1098 * @constructor 1099 * @type {IEventEmitter} 1100 * @example 1101 * var events = new EventEmitter(); 1102 * events.on("module:initialized", function(event){ console.log(event) }); 1103 * // then, somewhere else in the code... 1104 * events.trigger("module:initialized", { data }); 1105 * events.off("module:initialized") // clear all listeners 1106 */ 1107 function EventEmitter() { 1108 1109 /** 1110 * @this {IEventEmitter} 1111 */ 1112 this._listeners = Object.create(null); 1113 ;[ 1114 "addEventListener", 1115 "removeEventListener", 1116 "dispatchEvent", 1117 "on", 1118 "off", 1119 "trigger", 1120 "clear" 1121 ].forEach(function (fn) { 1122 this[fn] = this[fn].bind(this); 1123 }, this); 1124 1125 } 1126 1127 EventEmitter.prototype = { 1128 1129 /** 1130 * @description Adds a new listener that will be be called when parameter event <type> is dispatched 1131 * @param {string} type The name of an event to listen for 1132 * @param {EventListenerCallback} callback The function that will be called when the event is fired 1133 * @param {object|undefined} [context=undefined] The context that the function will be called withâin effect: `callback.call(context);` 1134 * @throws {Error} 1135 * @returns {Function} The function for unlistening to this event listener 1136 */ 1137 addEventListener: function (type, callback, context) { 1138 if (typeof type !== "string") { 1139 throw new Error("EventEmitter.addEventListener: argument 0 must be a string."); 1140 } 1141 1142 if (typeof callback === "undefined") { 1143 throw new Error("EventEmitter.addEventListener: argument 1 must be a function."); 1144 } 1145 1146 var that = this; 1147 this._listeners[type] = this._listeners[type] || []; 1148 /** 1149 * @var listener 1150 * @type {IEventListener} 1151 */ 1152 var listener = { 1153 type: type, 1154 callback: callback, 1155 context: context || null, 1156 cancel: function () { 1157 that.removeEventListener(type, callback); 1158 } 1159 }; 1160 1161 this._listeners[type].push(listener); 1162 1163 return listener.cancel; 1164 }, 1165 1166 /**
1167 * @description Removes a previously-defined listener 1168 * @param {string} type 1169 * @param {EventListenerCallback} callback 1170 * @returns {IEventEmitter} 1171 */ 1172 removeEventListener: function (type, callback) { 1173 1174 if (typeof type !== "string") { 1175 throw new Error("EventEmitter.removeEventListener: argument 0 must be a string."); 1176 } 1177 1178 // if callback is not provided, clear all listeners for that event 1179 if (typeof callback === "undefined") { 1180 this._listeners[type] = []; 1181 } 1182 else { 1183 1184 var eventList = (this._listeners[type] || []); 1185 // remove the event 1186 this._listeners[type] = eventList.filter(function (listener, i) { 1187 return (listener.callback !== callback); 1188 }); 1189 } 1190 return this; 1191 }, 1192 1193 /** 1194 * @description Clears ALL event listeners 1195 */ 1196 clear: function () { 1197 this._listeners = Object.create(null); 1198 }, 1199 1200 /** 1201 * @description Fires an event with the parameter type and data. 1202 * @param {string} type The event name to dispatch 1203 * @param {EventListenerData} [data=null] - Any additional data to be passed to the event callback 1204 * @returns {IEventListener[]} - An array of all the listener callbacks that were fired 1205 */ 1206 dispatchEvent: function (type, data) { 1207 1208 var timestamp = Date.now(); 1209 var listeners = (this._listeners[type] || []); 1210 1211 var wildcards = this._listeners[WILDCARD_CHAR] || []; 1212 1213 return listeners.concat(wildcards).map(function (event) { 1214 var eventDetails = { 1215 type: type, 1216 context: event.context, 1217 data: typeof data === "undefined" ? null : data, 1218 timestamp: timestamp 1219 }; 1220 1221 return event.callback.call(event.context, eventDetails); 1222 }); 1223 }, 1224 1225 /** 1226 * @alias addEventListener 1227 * @param {[string, EventListenerCallback]|[string, EventListenerCallback, EventListenerContext]} arguments 1228 * @returns {IEventEmitter} 1229 */ 1230 on: function () { 1231 return this.addEventListener.apply(this, arguments); 1232 }, 1233 1234 /** 1235 * @alias removeEventListener 1236 * @param {[string, EventListenerCallback]} arguments 1237 * @returns {IEventEmitter} 1238 */ 1239 off: function () { 1240 return this.removeEventListener.apply(this, arguments); 1241 }, 1242 1243 /** 1244 * @alias dispatchEvent 1245 * @param {[string]|[string, EventListenerData]} arguments 1246 * @returns {IEventListener[]} list of callbacks triggered 1247 */ 1248 trigger: function () { 1249 return this.dispatchEvent.apply(this, arguments); 1250 }, 1251 1252 /** 1253 * Add a listener that will only fire once 1254 * @param {string} type The name of an event to listen for 1255 * @param {EventListenerCallback} callback The function that will be called when the event is fired 1256 * @param {EventListenerContext} [context=null] The context that the function will be called withâin effect: `callback.call(context);` 1257 * @returns {Function} A function to cancel listening to the event 1258 */ 1259 once: function (type, callback, context) { 1260 1261 var removeCallback = this.on(type, function () { 1262 callback.apply(context, arguments); 1263 removeCallback(); 1264 }, context); 1265 1266 return removeCallback; 1267 } 1268 }; 1269 1270 1271 modules.EventEmitter = EventEmitter; 1272 1273})(window.wts, window.wts.modules); 1274; 1275 1276window.wts.UI_EVENTS = { 1277 1278 /** 1279 * When a user views a page â initial page views can be handled by analytics libraries 1280 * This event exists for pages loaded after the initial page viewâsuch as truck search's search results. 1281 */ 1282 PAGE_VIEW: "PAGE_VIEW", 1283 1284 /** 1285 * Fired from the document ready function of a page. Intended to be fired when a page has loaded. 1286 */ 1287 PAGE_LOAD: "PAGE_LOAD", 1288 1289 /** 1290 * Fired from various places within the UI when the user interacts with something on the page that triggers navigation or progression 1291 * to a new state. 1292 */ 1293 INTERACTION_CLICK: "INTERACTION_CLICK", 1294 1295 /** 1296 * Fired when the "See All Photos" link is clicked. 1297 */ 1298 SEE_MORE_INTERACTION_CLICK: "SEE_MORE_INTERACTION_CLICK", 1299 1300 /** 1301 * Handles interaction click events fired from controls targeted in the _Layout.cshtml file. 1302 */ 1303 GENERAL_LAYOUT_INTERACTION_CLICK: "GENERAL_LAYOUT_INTERACTION_CLICK", 1304 1305 /** 1306 * Fired when the vocational carousel changes pages. 1307 */ 1308 VOCATIONAL_CAROUSEL_PAGE_CHANGE: "VOCATIONAL_CAROUSEL_PAGE_CHANGE", 1309 1310 /** 1311 * Fired when either the items per page or sort by from the top/bottom navs changes. 1312 */ 1313 PAGINATION_CONTROL_CHANGE: "PAGINATION_CONTROL_CHANGE", 1314 1315 /** 1316 * Fired from various places within the UI when the user interacts with a form for the first time (presently only I'm Interested). 1317 */ 1318 FORM_START: "FORM_START", 1319 1320 /** 1321 * Fired when a form's contents change. 1322 */ 1323 FORM_ENGAGE: "FORM_ENGAGE", 1324 1325 /** 1326 * Clicking I'm interested generally should fire this 1327 */ 1328 OPEN_VEHICLE_INQUIRY_FORM: "OPEN_VEHICLE_INQUIRY_FORM", 1329 1330 /** 1331 * Submitting most "lead"-generating forms 1332 */ 1333 SUBMIT_VEHICLE_INQUIRY_FORM: "SUBMIT_VEHICLE_INQUIRY_FORM", 1334 1335 /** 1336 * An error occurred submitting most "lead"-generating forms 1337 */ 1338 SUBMIT_VEHICLE_INQUIRY_FORM_ERROR: "SUBMIT_VEHICLE_INQUIRY_FORM_ERROR", 1339 1340 /** 1341 * Submitting the load garage form 1342 **/ 1343 SUBMIT_LOAD_GARAGE_FORM: "SUBMIT_LOAD_GARAGE_FORM", 1344 1345 /** 1346 * Submitting the custom form 1347 **/ 1348 SUBMIT_CUSTOM_FORM: "SUBMIT_CUSTOM_FORM", 1349 1350 /** 1351 * The user has waited for the end of a form submission 1352 */ 1353 WAIT_FOR_FORM_CONFIRMATION: "WAIT_FOR_FORM_CONFIRMATION", 1354 1355 /** 1356 * Clicking the support link 1357 */ 1358 CLICK_SUPPORT_LINK: "CLICK_SUPPORT_LINK", 1359 1360 /** 1361 * Clicking call us now from the I'm Interested modal 1362 */ 1363 CALL_US_NOW_IM_INTERESTED: "CALL_US_NOW_IM_INTERESTED", 1364 1365 /** 1366 * Clicking the 'Request a Callback' button in the Call Us modal 1367 */ 1368 CALLBACK_REQUEST_CLICK: "CALLBACK_REQUEST_CLICK", 1369 1370 /** 1371 * Submitting the Support request form 1372 */ 1373 SUBMIT_SUPPORT_FORM: "SUBMIT_SUPPORT_FORM", 1374 1375 /** 1376 * A Ford Direct Milestone 4 overlay opened. Data: { overlayKey, inventoryID }. 1377 * overlayKey must exist in the OVERLAY_CATALOG in ford-direct-analytics.js. 1378 */ 1379 FORD_OVERLAY_OPEN: "FORD_OVERLAY_OPEN", 1380 1381 /** 1382 * A Ford Direct Milestone 4 overlay closed. Data: { overlayKey }. 1383 * Keyed so that closing an unrelated modal cannot discard an overlay still open beneath it. 1384 */ 1385 FORD_OVERLAY_CLOSE: "FORD_OVERLAY_CLOSE", 1386 1387 /** 1388 * Interactions that open the photo gallery 1389 */ 1390 PHOTO_GALLERY_OPEN: "PHOTO_GALLERY_OPEN", 1391 1392 /** 1393 * Interactions that close the photo gallery 1394 */ 1395 PHOTO_GALLERY_CLOSE: "PHOTO_GALLERY_CLOSE", 1396 1397 /** 1398 * Within the Photo Gallery, interactions that view the next photo 1399 */ 1400 PHOTO_GALLERY_NEXT_PHOTO: "PHOTO_GALLERY_NEXT_PHOTO", 1401 1402 /** 1403 * Within the Photo Gallery, interactions that view the previous photo 1404 */
1405 PHOTO_GALLERY_PREVIOUS_PHOTO: "PHOTO_GALLERY_PREVIOUS_PHOTO", 1406 1407 /** 1408 * Within the Photo Gallery, interactions that view the next page of thumbnails 1409 */ 1410 PHOTO_GALLERY_NEXT_PAGE: "PHOTO_GALLERY_NEXT_PAGE", 1411 1412 /** 1413 * Within the Photo Gallery, interactions that view the previous page of thumbnails 1414 */ 1415 PHOTO_GALLERY_PREVIOUS_PAGE: "PHOTO_GALLERY_PREVIOUS_PAGE", 1416 1417 /** 1418 * Within the Photo Gallery, interactions that view the next photo 1419 */ 1420 FEATURED_PHOTO_GALLERY_NEXT_PHOTO: "FEATURED_PHOTO_GALLERY_NEXT_PHOTO", 1421 1422 /** 1423 * Within the Photo Gallery, interactions that view the previous photo 1424 */ 1425 FEATURED_PHOTO_GALLERY_PREVIOUS_PHOTO: "FEATURED_PHOTO_GALLERY_PREVIOUS_PHOTO", 1426 1427 /** 1428 * On the VDP featured gallery, clicking the large active photo to open the lightbox 1429 */ 1430 FEATURED_PHOTO_GALLERY_VIEW_PHOTO: "FEATURED_PHOTO_GALLERY_VIEW_PHOTO", 1431 1432 /** 1433 * Within the Photo Gallery, interactions that view the next page of thumbnails 1434 */ 1435 FEATURED_PHOTO_GALLERY_NEXT_PAGE: "FEATURED_PHOTO_GALLERY_NEXT_PAGE", 1436 1437 /** 1438 * Within the Photo Gallery, interactions that view the previous page of thumbnails 1439 */ 1440 FEATURED_PHOTO_GALLERY_PREVIOUS_PAGE: "FEATURED_PHOTO_GALLERY_PREVIOUS_PAGE", 1441 1442 /** 1443 * Within the Photo Gallery, clicking a thumbnail 1444 */ 1445 PHOTO_GALLERY_CLICK_THUMBNAIL: "PHOTO_GALLERY_CLICK_THUMBNAIL", 1446 1447 /** 1448 * Within the Photo Gallery, clicking the 'open in new tab' link 1449 */ 1450 PHOTO_GALLERY_VIEW_PHOTO_NEW_TAB: "PHOTO_GALLERY_VIEW_PHOTO_NEW_TAB", 1451 1452 /** 1453 * Within the VDP, clicking a thumbnail related to the featured photo 1454 */ 1455 CLICK_FEATURED_PHOTO_THUMB: "CLICK_FEATURED_PHOTO_THUMB", 1456 1457 /** 1458 * When clicking a link to view a vehicle's condition 1459 */ 1460 CLICK_CONDITION_LINK: "CLICK_CONDITION_LINK", 1461 1462 /** 1463 * Clicking an outbound link related to a vehicle 1464 */ 1465 CLICK_OUTBOUND_LINK: "CLICK_OUTBOUND_LINK", 1466 1467 /** 1468 * Clicking a vehicle's window sticker link 1469 */ 1470 CLICK_WINDOWSTICKER_LINK: "CLICK_WINDOWSTICKER_LINK", 1471 1472 /** 1473 * Clicking a vehicle's body link to a datasheet 1474 */ 1475 CLICK_BODY_DATASHEET_LINK: "CLICK_BODY_DATASHEET_LINK", 1476 1477 /** 1478 * Clicking a vehicle's chassis link to a datasheet 1479 */ 1480 CLICK_CHASSIS_DATASHEET_LINK: "CLICK_CHASSIS_DATASHEET_LINK", 1481 1482 /** 1483 * Clicking a vehicle's auction link 1484 */ 1485 CLICK_AUCTION_LINK: "CLICK_AUCTION_LINK", 1486 1487 /** 1488 * Clicking a VSP or VAST vehicle listing photo 1489 */ 1490 CLICK_VEHICLE_LISTING_PHOTO: "CLICK_VEHICLE_LISTING_PHOTO", 1491 1492 /** 1493 * Clicking a VSP or VAST vehicle listing title 1494 */ 1495 CLICK_VEHICLE_LISTING_TITLE: "CLICK_VEHICLE_LISTING_TITLE", 1496 1497 /** 1498 * Clicking a VSP or VAST vehicle listing "see more details" link 1499 */ 1500 CLICK_VEHICLE_LISTING_SEE_DETAILS: "CLICK_VEHICLE_LISTING_SEE_DETAILS", 1501 1502 /** 1503 * Clicking a CTA to request an upfit 1504 */ 1505 CLICK_REQUEST_UPFIT: "CLICK_REQUEST_UPFIT", 1506 1507 /** 1508 * Clicking a "more info" button related to an upfit package 1509 */ 1510 CLICK_REQUEST_UPFIT_INFO: "CLICK_REQUEST_UPFIT_INFO", 1511 1512 /** 1513 * Clicking a spec sheet related to an upfit package 1514 */ 1515 CLICK_UPFIT_SPECIFICATION_SHEET: "CLICK_UPFIT_SPECIFICATION_SHEET", 1516 1517 /** 1518 * Clicking on the view dealership website link on convoy 1519 */ 1520 CLICK_VDP_COMVOY_WEBSITE_LINK: "CLICK_VDP_COMVOY_WEBSITE_LINK", 1521 1522 /** 1523 * Choosing a vocation related to van builder or upfit requests 1524 */ 1525 SELECT_UPFIT_VOCATION: "SELECT_UPFIT_VOCATION", 1526 1527 /** 1528 * Selecting an upfit package 1529 */ 1530 SELECT_UPFIT_PACKAGE: "SELECT_UPFIT_PACKAGE", 1531 1532 /** 1533 * Exiting a request upfit form or clicking to return to other packages 1534 */ 1535 CLICK_REMOVE_SELECTED_PACKAGE: "CLICK_REMOVE_SELECTED_PACKAGE", 1536 1537 /** 1538 * Clicking to submit a Request Upfit form 1539 */ 1540 SUBMIT_REQUEST_UPFIT_FORM: "SUBMIT_REQUEST_UPFIT_FORM", 1541 1542 /** 1543 * Adding a vocation to the van builder form 1544 */ 1545 SELECT_VAN_BUILDER_VOCATION: "SELECT_VAN_BUILDER_VOCATION", 1546 1547 /** 1548 * Dealer Locator Search tracking 1549 */ 1550 SELECT_DEALER_LOCATOR_SEARCH: "SELECT_DEALER_LOCATOR_SEARCH", 1551 1552 /** 1553 * Advancing the dealer locator search page when fetching more results 1554 */ 1555 SELECT_DEALER_LOCATOR_SEARCH_PAGE: "SELECT_DEALER_LOCATOR_SEARCH_PAGE", 1556 1557 /** 1558 * Conditional search events that could occur on a dealer locator. 1559 */ 1560 SELECT_DEALER_LOCATOR_SEARCH_CONDITIONAL: "SELECT_DEALER_LOCATOR_SEARCH_CONDITIONAL", 1561 1562 /** 1563 * Checking the checkbox to show portal dealer's inventory on VAST. 1564 */ 1565 CLICK_SHOW_MY_INVENTORY_ON_VAST: "CLICK_SHOW_MY_INVENTORY_ON_VAST", 1566 1567 /** 1568 * Checking the checkbox to show sister inventory on VAST. 1569 */ 1570 CLICK_SHOW_SISTER_INVENTORY_ON_VAST: "CLICK_SHOW_SISTER_INVENTORY_ON_VAST", 1571 1572 /**
1573 * Clicking the 'AVAILABLE FOR PURCHASE' tab on VAST. 1574 */ 1575 CLICK_AVAILABLE_FOR_PURCHASE_TAB_ON_VAST: "CLICK_AVAILABLE_FOR_PURCHASE_TAB_ON_VAST", 1576 1577 /** 1578 * Clicking the 'WHOLE WTS NETWORK' tab on VAST. 1579 */ 1580 CLICK_WHOLE_WTS_NETWORK_TAB_ON_VAST: "CLICK_WHOLE_WTS_NETWORK_TAB_ON_VAST", 1581 1582 /** 1583 * Clicking the 'BRIGHTDROP POOL' tab on VAST. 1584 */ 1585 CLICK_BRIGHTDROP_POOL_TAB_ON_VAST: "CLICK_BRIGHTDROP_POOL_TAB_ON_VAST", 1586 1587 /** 1588 * Checking the checkbox to show group inventory on VAST. 1589 */ 1590 CLICK_SHOW_GROUP_INVENTORY_ON_VAST: "CLICK_SHOW_GROUP_INVENTORY_ON_VAST", 1591 1592 /** 1593 * Clicking the email of a trade contact 1594 */ 1595 CLICK_EMAIL_VAST_CONTACT: "CLICK_EMAIL_VAST_CONTACT", 1596 1597 /** 1598 * Clicking the phone number of a trade contact 1599 */ 1600 CLICK_CALL_VAST_CONTACT: "CLICK_CALL_VAST_CONTACT", 1601 1602 /** 1603 * Clicking the phone number of a dealer related to a VAST vehicle 1604 */ 1605 CLICK_CALL_VAST_DEALER: "CLICK_CALL_VAST_DEALER", 1606 1607 /** 1608 * Clicking the condition url of a VAST vehicle 1609 */ 1610 CLICK_VAST_CONDITION_LINK: "CLICK_VAST_CONDITION_LINK", 1611 1612 /** 1613 * Clicking the auction url of a VAST vehicle 1614 */ 1615 CLICK_VAST_AUCTION_LINK: "CLICK_VAST_AUCTION_LINK", 1616 1617 /** 1618 * Clicking to submit a message to send to a VAST contact 1619 */ 1620 SUBMIT_VAST_MESSAGE: "SUBMIT_VAST_MESSAGE", 1621 1622 /** 1623 * Clicking an external link 1624 */ 1625 CLICK_VAST_OUTBOUND_LINK: "CLICK_VAST_OUTBOUND_LINK", 1626 1627 /** 1628 * Clicking to view the 'trade details' / VAST details of a VAST vehicle should trigger this 1629 */ 1630 OPEN_VEHICLE_ACQUISITION_DETAILS: "OPEN_VEHICLE_ACQUISITION_DETAILS", 1631 1632 /** 1633 * Clicking a "back to search" link or button 1634 */ 1635 CLICK_BACK_TO_SEARCH_RESULTS: "CLICK_BACK_TO_SEARCH_RESULTS", 1636 1637 /** 1638 * CLicking "Get a quote" for a Bare Chassis 1639 */ 1640 CLICK_BARE_CHASSIS_INQUIRY: "CLICK_BARE_CHASSIS_INQUIRY", 1641 1642 /** 1643 * CLicking "Get a quote" for a Body Only 1644 */ 1645 CLICK_BODY_ONLY_INQUIRY: "CLICK_BODY_ONLY_INQUIRY", 1646 1647 /** 1648 * CLicking the "Save to Garage" button 1649 */ 1650 CLICK_SAVE_VEHICLE_TO_GARAGE: "CLICK_SAVE_VEHICLE_TO_GARAGE", 1651 1652 /** 1653 * Clicking a vehicle ad from the ad loader. 1654 */ 1655 CLICK_VEHICLE_AD: "CLICK_VEHICLE_AD", 1656 1657 /** 1658 * Clicking a recently viewed vehicle from the recently viewed carousel. 1659 */ 1660 CLICK_RECENTLY_VIEWED: "CLICK_RECENTLY_VIEWED", 1661 1662 /** 1663 * Perform a conditional search for a truck. 1664 */ 1665 TRUCK_SEARCH_EVENT_CONDITIONAL: "TRUCK_SEARCH_EVENT_CONDITIONAL", 1666 1667 /** 1668 * Perform a vin search. 1669 */ 1670 TRUCK_SEARCH_EVENT_VIN: "TRUCK_SEARCH_EVENT_VIN", 1671 1672 TRUCK_SEARCH_EVENT_RANGE_CONDITIONAL: "TRUCK_SEARCH_EVENT_RANGE_CONDITIONAL", 1673 1674 MILES_SEARCH_CONDITIONAL: "MILES_SEARCH_CONDITIONAL", 1675 1676 LOCATION_SEARCH_CONDITIONAL: "LOCATION_SEARCH_CONDITIONAL", 1677 1678 VOCATION_SEARCH_CONDITIONAL: "VOCATION_SEARCH_CONDITIONAL", 1679 1680 TRUCK_SEARCH_CLEAR_FILTERS: "TRUCK_SEARCH_CLEAR_FILTERS", 1681 1682 TRUCK_SEARCH_ZIPCODE_CHANGE: "TRUCK_SEARCH_ZIPCODE_CHANGE", 1683 1684 /** 1685 * Searching for a truck returns no results. 1686 */ 1687 TRUCK_SEARCH_EVENT_NO_RESULTS: "TRUCK_SEARCH_EVENT_NO_RESULTS", 1688 1689 /** 1690 * Fired after a VSP search completes and the new results are rendered. 1691 */ 1692 VSP_SEARCH_COMPLETE: "VSP_SEARCH_COMPLETE", 1693 1694 /** 1695 * Clicking on truck search facets within vast. 1696 */ 1697 CLICK_TRUCK_SEARCH_FACETS_VAST: "CLICK_TRUCK_SEARCH_FACETS_VAST", 1698 1699 /** 1700 * Events that can come from the about page. 1701 */ 1702 ABOUT_EVENT: "ABOUT_EVENT", 1703 1704 /** 1705 * Clicking a custom page's email dealer button. 1706 */ 1707 CLICK_CUSTOM_PAGE_EMAIL_DEAILER: "CLICK_CUSTOM_PAGE_EMAIL_DEAILER", 1708 1709 TRACK_CLOSE_FORM_EVENT: "TRACK_CLOSE_FORM_EVENT", 1710 /** 1711 * Tracks various vehicle events occuring from vehicle details. 1712 */ 1713 VEHICLE_DETAILS_TRACK_VEHICLE_EVENT: "VEHICLE_DETAILS_TRACK_VEHICLE_EVENT", 1714 1715 /** 1716 * Tracks various vehicle branding events occuring from vehicle details. 1717 */ 1718 VEHICLE_DETAILS_TRACK_BRANDING_EVENT: "VEHICLE_DETAILS_TRACK_BRANDING_EVENT", 1719 1720 /**
1721 * Track when the dealer video youtube link has been clicked. 1722 */ 1723 VEHICLE_DETAILS_TRACK_DEALER_VIDEO_EVENT: "VEHICLE_DETAILS_TRACK_DEALER_VIDEO_EVENT", 1724 1725 /** 1726 * Track when the dealer video youtube link has been clicked. 1727 */ 1728 VEHICLE_DETAILS_TRACK_BODY_DETAILS_VIDEO_EVENT: "VEHICLE_DETAILS_TRACK_BODY_DETAILS_VIDEO_EVENT", 1729 1730 VEHICLE_DETAILS_TRACK_BODY_MANUFACTURER_VIDEO_EVENT: "VEHICLE_DETAILS_TRACK_BODY_MANUFACTURER_VIDEO_EVENT", 1731 1732 VEHICLE_DETAILS_CLOSE_VIDEO_EVENT: "VEHICLE_DETAILS_CLOSE_VIDEO_EVENT", 1733 1734 /** 1735 * Track when internal links has been clicked 1736 */ 1737 VEHICLE_DETAILS_INTERNAL_LINK_CLICK: "VEHICLE_DETAILS_INTERNAL_LINK_CLICK", 1738 1739 /** 1740 * Track when the vehicles notes read more link has been clicked 1741 */ 1742 READ_MORE_VEHICLE_NOTES_CLICK: "READ_MORE_VEHICLE_NOTES_CLICK", 1743 1744 /** 1745 * Track when the vehicles notes read less link has been clicked 1746 */ 1747 READ_LESS_VEHICLE_NOTES_CLICK: "READ_LESS_VEHICLE_NOTES_CLICK", 1748 1749 /** 1750 * Track when the vehicle options view all link has been clicked 1751 */ 1752 VIEW_ALL_VEHICLE_OPTIONS_CLICK: "VIEW_ALL_VEHICLE_OPTIONS_CLICK", 1753 1754 /** 1755 * Track when the see dealership's other listings link has been clicked 1756 */ 1757 SEE_DEALERSHIPS_OTHER_LISTINGS_CLICK: "SEE_DEALERSHIPS_OTHER_LISTINGS_CLICK", 1758 1759 1760 /** 1761 * Tracks various garage events occurring from my garage. 1762 */ 1763 MY_GARAGE_GARAGE_EVENT: "MY_GARAGE_GARAGE_EVENT", 1764 1765 /** 1766 * Tracks various vehicle events occurring from my garage. 1767 */ 1768 MY_GARAGE_VEHICLE_EVENT: "MY_GARAGE_VEHICLE_EVENT", 1769 1770 /** 1771 * Tracks various search events occurring from inventory layout. 1772 */ 1773 INVENTORY_LAYOUT_TRACK_SEARCH_EVENT: "INVENTORY_LAYOUT_TRACK_SEARCH_EVENT", 1774 1775 /** 1776 * Tracks various locator vehicle search events from inventory layout. 1777 */ 1778 INVENTORY_LAYOUT_TRACK_LOCATOR_SEARCH_EVENT: "INVENTORY_LAYOUT_TRACK_LOCATOR_SEARCH_EVENT", 1779 1780 /** 1781 * Tracks Facetgroup events 1782 */ 1783 VSP_FACETGROUP_STATE_OPEN_EVENT: "VSP_FACETGROUP_STATE_OPEN_EVENT", 1784 1785 VSP_FACETGROUP_STATE_CLOSE_EVENT: "VSP_FACETGROUP_STATE_CLOSE_EVENT", 1786 1787 VSP_FACETGROUP_CHASSIS_OPEN_EVENT: "VSP_FACETGROUP_CHASSIS_OPEN_EVENT", 1788 1789 VSP_FACETGROUP_CHASSIS_CLOSE_EVENT: "VSP_FACETGROUP_CHASSIS_CLOSE_EVENT", 1790 1791 VSP_FACETGROUP_BODY_OPEN_EVENT: "VSP_FACETGROUP_BODY_OPEN_EVENT", 1792 1793 VSP_FACETGROUP_BODY_CLOSE_EVENT: "VSP_FACETGROUP_BODY_CLOSE_EVENT", 1794 1795 VSP_FACETGROUP_ADDONS_OPEN_EVENT: "VSP_FACETGROUP_ADDONS_OPEN_EVENT", 1796 1797 VSP_FACETGROUP_ADDONS_CLOSE_EVENT: "VSP_FACETGROUP_ADDONS_CLOSE_EVENT", 1798 1799 VSP_FACETGROUP_TRAILER_OPEN_EVENT: "VSP_FACETGROUP_TRAILER_OPEN_EVENT", 1800 1801 VSP_FACETGROUP_TRAILER_CLOSE_EVENT: "VSP_FACETGROUP_TRAILER_CLOSE_EVENT", 1802 1803 VSP_FACETGROUP_PRICE_OPEN_EVENT: "VSP_FACETGROUP_PRICE_OPEN_EVENT", 1804 1805 VSP_FACETGROUP_PRICE_CLOSE_EVENT: "VSP_FACETGROUP_PRICE_CLOSE_EVENT", 1806 1807 VSP_FACETGROUP_PROGRESSIVE_OPEN_EVENT: "VSP_FACETGROUP_PROGRESSIVE_OPEN_EVENT", 1808 1809 VSP_FACETGROUP_PROGRESSIVE_CLOSE_EVENT: "VSP_FACETGROUP_PROGRESSIVE_CLOSE_EVENT", 1810 1811 VAST_VSP_FACETGROUP_PARTNER_OPEN_EVENT: "VAST_VSP_FACETGROUP_PARTNER_OPEN_EVENT", 1812 1813 VAST_VSP_FACETGROUP_PARTNER_CLOSE_EVENT: "VAST_VSP_FACETGROUP_PARTNER_CLOSE_EVENT", 1814 1815 /** 1816 * Tracks Facetgroup events 1817 */ 1818 VSP_FACET_OPEN_EVENT: "VSP_FACET_OPEN_EVENT", 1819 1820 VSP_FACET_CLOSE_EVENT: "VSP_FACET_CLOSE_EVENT", 1821 1822 /* 1823 * Tracks changing the number of items per page on the VSP 1824 */ 1825 VSP_CHANGE_ITEMS_PER_PAGE: "VSP_CHANGE_ITEMS_PER_PAGE", 1826 1827 VSP_CHANGE_SORT: "VSP_CHANGE_SORT", 1828 1829 /** 1830 * Tracks EZOrder events 1831 */ 1832 EZORDER_EVENT: "EZORDER_EVENT", 1833 1834 /** 1835 * Tracks Custom Order events 1836 */ 1837 CUSTOM_ORDER_EVENT: "CUSTOM_ORDER_EVENT", 1838 1839 /** 1840 * Tracks CV Showroom events 1841 */ 1842 CVSHOWROOM_EVENT: "CVSHOWROOM_EVENT", 1843 1844 /** 1845 * Tracks Taycor events 1846 */ 1847 TAYCOR_EVENT: "TAYCOR_EVENT", 1848 1849 /** 1850 * Tracks Crestline events 1851 */ 1852 CRESTLINE_EVENT: "CRESTLINE_EVENT", 1853 1854 /** 1855 * Tracks WebBuy events 1856 */ 1857 WEBBUY_EVENT: "WEBBUY_EVENT", 1858 1859 /** 1860 * Navigation Page Events 1861 */ 1862 NAVIGATION_PAGE_PARTNER_LEARN_MORE: "NAVIGATION_PAGE_PARTNER_LEARN_MORE", 1863 NAVIGATION_PAGE_VIEW_ALL_PARTNERS: "NAVIGATION_PAGE_VIEW_ALL_PARTNERS", 1864 1865 /** 1866 * Tracks Marketplace events 1867 */ 1868 MARKETPLACE_EVENT: "MARKETPLACE_EVENT", 1869 1870 /** 1871 * Custom Orders Catalog Events 1872 */ 1873 COMMERCIAL_CATALOG_TOGGLE: "COMMERCIAL_CATALOG_TOGGLE", 1874 COMMERCIAL_CATALOG_VIEW_BODY_LINE_LITERATURE_PDF: "COMMERCIAL_CATALOG_VIEW_BODY_LINE_LITERATURE_PDF", 1875 COMMERCIAL_CATALOG_VIEW_BODY_LINE_VIDEO: "COMMERCIAL_CATALOG_VIEW_BODY_LINE_VIDEO", 1876 COMMERCIAL_CATALOG_START_REQUEST: "COMMERCIAL_CATALOG_START_REQUEST", 1877 COMMERCIAL_CATALOG_VIEW_PHOTO: "COMMERCIAL_CATALOG_VIEW_PHOTO", 1878 COMMERCIAL_CATALOG_NEXT_PHOTO: "COMMERCIAL_CATALOG_NEXT_PHOTO",
1879 COMMERCIAL_CATALOG_PREV_PHOTO: "COMMERCIAL_CATALOG_PREV_PHOTO", 1880 COMMERCIAL_CATALOG_CHANGE_BODY_TYPE: "COMMERCIAL_CATALOG_CHANGE_BODY_TYPE", 1881 COMMERCIAL_CATALOG_CLICK_BODY_LINE: "COMMERCIAL_CATALOG_CLICK_BODY_LINE", 1882 COMMERCIAL_CATALOG_CLICK_SHOW_MORE: "COMMERCIAL_CATALOG_CLICK_SHOW_MORE", 1883 COMMERCIAL_CATALOG_CLICK_SHOW_MORE: "COMMERCIAL_CATALOG_CLICK_SHOW_MORE", 1884 COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_NEXT: "COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_NEXT", 1885 COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_PREVIOUS: "COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_PREVIOUS", 1886 1887 /** 1888 * Conditional Call To Action Events 1889 */ 1890 CONDITIONAL_CALL_TO_ACTION: "CONDITIONAL_CALL_TO_ACTION", 1891 1892 /** 1893 * Resource Rack Catalog Events 1894 */ 1895 RESOURCE_RACK_LAUNCH_PAGE: "RESOURCE_RACK_LAUNCH_PAGE", 1896 RESOURCE_RACK_CHANGE_SEARCH_CONTROLS: "RESOURCE_RACK_CHANGE_SEARCH_CONTROLS", 1897 RESOURCE_RACK_PREVIEW_LINK_CLICK: "RESOURCE_RACK_PREVIEW_LINK_CLICK", 1898 RESOURCE_RACK_ACTION_MENU_GET_LINK: "RESOURCE_RACK_ACTION_MENU_GET_LINK", 1899 RESOURCE_RACK_MULTISELECT_FOOTER_ACTION_COPY_LINKS: "RESOURCE_RACK_MULTISELECT_FOOTER_ACTION_COPY_LINKS", 1900 RESOURCE_RACK_MULTISELECT_FOOTER_ACTION_START_PROSPECTING: "RESOURCE_RACK_MULTISELECT_FOOTER_ACTION_START_PROSPECTING", 1901 1902 /** 1903 * Tracking Links History Events 1904 */ 1905 TRACKING_LINKS_HISTORY_LAUNCH_PAGE: "TRACKING_LINKS_HISTORY_LAUNCH_PAGE", 1906 TRACKING_LINKS_HISTORY_CREATE_TRACKING_LINK: "TRACKING_LINKS_HISTORY_CREATE_TRACKING_LINK", 1907 TRACKING_LINKS_HISTORY_SUBMIT_TRACKING_LINK: "TRACKING_LINKS_HISTORY_SUBMIT_TRACKING_LINK", 1908 TRACKING_LINKS_HISTORY_CLICK_DETAILS: "TRACKING_LINKS_HISTORY_CLICK_DETAILS", 1909 TRACKING_LINKS_HISTORY_CLICK_EDIT_TRACKING_LINK: "TRACKING_LINKS_HISTORY_CLICK_EDIT_TRACKING_LINK", 1910 TRACKING_LINKS_HISTORY_CLICK_SAVE_EDIT: "TRACKING_LINKS_HISTORY_CLICK_SAVE_EDIT", 1911 TRACKING_LINKS_HISTORY_CLICK_DISABLE_LINK: "TRACKING_LINKS_HISTORY_CLICK_DISABLE_LINK", 1912 TRACKING_LINKS_HISTORY_CLICK_COPY_LINK: "TRACKING_LINKS_HISTORY_CLICK_COPY_LINK", 1913 1914 /** 1915 * Tracking Links Email 1916 */ 1917 TRACKING_LINKS_EMAIL_CLICK_SEE_LINKS: "TRACKING_LINKS_EMAIL_CLICK_SEE_LINKS", 1918 1919 /** 1920 * Invoice Binder 1921 */ 1922 INVOICE_BINDER_LAUNCH_PAGE: "INVOICE_BINDER_LAUNCH_PAGE", 1923 INVOICE_BINDER_SEARCH: "INVOICE_BINDER_SEARCH", 1924 INVOICE_BINDER_CLICK_ON_COUNT: "INVOICE_BINDER_CLICK_ON_COUNT", 1925 INVOICE_BINDER_CLICK_VIEW_LISTING_DETAILS: "INVOICE_BINDER_CLICK_VIEW_LISTING_DETAILS", 1926 INVOICE_BINDER_CLICK_MARK_IF_NOT_UPFIT: "INVOICE_BINDER_CLICK_MARK_IF_NOT_UPFIT", 1927 INVOICE_BINDER_CLICK_WINDOW_STICKER: "INVOICE_BINDER_CLICK_WINDOW_STICKER", 1928 INVOICE_BINDER_CLICK_INVOICE_LINK: "INVOICE_BINDER_CLICK_INVOICE_LINK", 1929 INVOICE_BINDER_CLICK_UPLOAD_INVOICES: "INVOICE_BINDER_CLICK_UPLOAD_INVOICES", 1930 INVOICE_BINDER_CLICK_DELETE_INVOICE: "INVOICE_BINDER_CLICK_DELETE_INVOICE", 1931 1932 /** 1933 * PDF viewer 1934 */ 1935 PDF_VIEWER_INTERNAL_LOADING: "PDF_VIEWER_INTERNAL_LOADING", 1936 PDF_VIEWER_CLICK_GET_SHAREABLE_LINK: "PDF_VIEWER_CLICK_GET_SHAREABLE_LINK", 1937 PDF_VIEWER_CLICK_GET_TRACKING_LINK: "PDF_VIEWER_CLICK_GET_TRACKING_LINK", 1938 1939 1940 /** 1941 * PRINTING 1942 */ 1943 PRINTING_ACTION_PRINT_COMPLETE_LISTING: "PRINTING_ACTION_PRINT_COMPLETE_LISTING", 1944 PRINTING_ACTION_PRINT_UPFIT_WINDOW_STICKER: "PRINTING_ACTION_PRINT_UPFIT_WINDOW_STICKER", 1945 1946 /** 1947 * Inventory Management 1948 */ 1949 // General 1950 INVENTORY_MANAGEMENT_GROUP_SELECT_MEMBER_DEALER: 'INVENTORY_MANAGEMENT_GROUP_SELECT_MEMBER_DEALER', 1951 INVENTORY_MANAGEMENT_EXPORT_LISTINGS: 'INVENTORY_MANAGEMENT_EXPORT_LISTINGS', 1952 // Create Listing 1953 INVENTORY_MANAGEMENT_CREATE_BODY_ONLY_LISTING: "INVENTORY_MANAGEMENT_CREATE_BODY_ONLY_LISTING", 1954 INVENTORY_MANAGEMENT_CREATE_TRAILER_LISTING: "INVENTORY_MANAGEMENT_CREATE_TRAILER_LISTING", 1955 INVENTORY_MANAGEMENT_CREATE_VEHICLE_LISTING: "INVENTORY_MANAGEMENT_CREATE_VEHICLE_LISTING", 1956 // Inventory Summary 1957 INVENTORY_MANAGEMENT_CLICK_TOTAL_LISTINGS: "INVENTORY_MANAGEMENT_CLICK_TOTAL_LISTINGS", 1958 INVENTORY_MANAGEMENT_CLICK_ON_SALE_LISTINGS: "INVENTORY_MANAGEMENT_CLICK_ON_SALE_LISTINGS", 1959 INVENTORY_MANAGEMENT_CLICK_FEATURED_LISTINGS: "INVENTORY_MANAGEMENT_CLICK_FEATURED_LISTINGS", 1960 INVENTORY_MANAGEMENT_CLICK_HIDDEN_LISTINGS: "INVENTORY_MANAGEMENT_CLICK_HIDDEN_LISTINGS", 1961 INVENTORY_MANAGEMENT_CLICK_PRIVATE_LISTINGS: "INVENTORY_MANAGEMENT_CLICK_PRIVATE_LISTINGS", 1962 INVENTORY_MANAGEMENT_CLICK_VAST_ONLY_LISTINGS: "INVENTORY_MANAGEMENT_CLICK_VAST_ONLY_LISTINGS", 1963 INVENTORY_MANAGEMENT_CLICK_UNKNOWN_IF_UPFIT: "INVENTORY_MANAGEMENT_CLICK_UNKNOWN_IF_UPFIT", 1964 INVENTORY_MANAGEMENT_CLICK_MISSING_PHOTOS: "INVENTORY_MANAGEMENT_CLICK_MISSING_PHOTOS", 1965 INVENTORY_MANAGEMENT_CLICK_INVALID_PHOTOS: "INVENTORY_MANAGEMENT_CLICK_INVALID_PHOTOS", 1966 INVENTORY_MANAGEMENT_CLICK_NO_INVOICES: "INVENTORY_MANAGEMENT_CLICK_NO_INVOICES", 1967 INVENTORY_MANAGEMENT_CLICK_MANUAL_LISTINGS: "INVENTORY_MANAGEMENT_CLICK_MANUAL_LISTINGS", 1968 // Inventory Search 1969 INVENTORY_MANAGEMENT_TOGGLE_ADVANCED_SEARCH: "INVENTORY_MANAGEMENT_TOGGLE_ADVANCED_SEARCH", 1970 INVENTORY_MANAGEMENT_RUN_LAST_QUERY: 'INVENTORY_MANAGEMENT_RUN_LAST_QUERY', 1971 INVENTORY_MANAGEMENT_TEXT_SEARCH: "INVENTORY_MANAGEMENT_TEXT_SEARCH", 1972 INVENTORY_MANAGEMENT_FILTER_INVENTORY_SELECT: "INVENTORY_MANAGEMENT_FILTER_INVENTORY_SELECT", 1973 INVENTORY_MANAGEMENT_FILTER_INVENTORY_CHECKBOX: "INVENTORY_MANAGEMENT_FILTER_INVENTORY_CHECKBOX", 1974 INVENTORY_MANAGEMENT_RESET_FILTERS: "INVENTORY_MANAGEMENT_RESET_FILTERS", 1975 // Action Menu 1976 INVENTORY_MANAGEMENT_CLICK_VIEW_INVOICES: "INVENTORY_MANAGEMENT_CLICK_VIEW_INVOICES", 1977 INVENTORY_MANAGEMENT_CLICK_EDIT_PRICING: "INVENTORY_MANAGEMENT_CLICK_EDIT_PRICING", 1978 INVENTORY_MANAGEMENT_CLICK_EDIT_TRADE_DETAILS: "INVENTORY_MANAGEMENT_CLICK_EDIT_TRADE_DETAILS", 1979 INVENTORY_MANAGEMENT_CLICK_EDIT_LISTING: "INVENTORY_MANAGEMENT_CLICK_EDIT_LISTING", 1980 INVENTORY_MANAGEMENT_CLICK_MARK_AS_MOVED: "INVENTORY_MANAGEMENT_CLICK_MARK_AS_MOVED", 1981 INVENTORY_MANAGEMENT_CLICK_DELETE_LISTING: "INVENTORY_MANAGEMENT_CLICK_DELETE_LISTING", 1982 // Youtube Video Player 1983 VIDEO_PLAYER_START: "VIDEO_PLAYER_START", 1984 VIDEO_PLAYER_RESUME: "VIDEO_PLAYER_RESUME", 1985 VIDEO_PLAYER_PAUSE: "VIDEO_PLAYER_PAUSE", 1986 VIDEO_PLAYER_THRESHOLD: "VIDEO_PLAYER_THRESHOLD" 1987};; 1988(function (wts, modules) { 1989 1990 var EventEmitter = modules.EventEmitter; 1991 1992 function Visor() { 1993 EventEmitter.call(this); 1994 } 1995 1996 Visor.prototype = Object.assign(EventEmitter.prototype, { 1997 1998 /** 1999 * @static 2000 */ 2001 Events: wts.UI_EVENTS, 2002 2003 /** 2004 * @alias trigger 2005 * @returns {any[]} The results of the listeners 2006 */ 2007 fire: function () { 2008 return this.trigger.apply(this, arguments); 2009 }, 2010 2011 /** 2012 * Fires an event and allows you to wait until all listeners to that event are done 2013 * @async 2014 * @param {string} type The event name 2015 * @param {any}
2015 data Event Data to be passed to listeners of this event 2016 * @returns {Promise} A promise that resolves with all listener promises 2017 */ 2018 fireAndWait: function (type, data) { 2019 2020 // filter out any listeners that are not a Thenable 2021 var promises = this.trigger(type, data) 2022 .filter(function (result) { 2023 var hasResult = result !== null && typeof result !== "undefined"; 2024 var isThenable = false; 2025 if (hasResult) { 2026 isThenable = typeof result.promise !== "undefined"; 2027 } 2028 return hasResult && isThenable; 2029 }); 2030 2031 return $.when.apply(null, promises); 2032 } 2033 2034 }); 2035 2036 // export as singleton 2037 wts.visor = new Visor(); 2038 2039})(window.wts, window.wts.modules);; 2040(function (wts, modules) { 2041 /** 2042 * The location of any global visor event bindings 2043 * The UI should hook into visor here. 2044 * 2045 */ 2046 2047 var UI_EVENTS = wts.visor.Events; 2048 2049 UI_EVENTS; 2050 2051 wts.ready.then(function () { 2052 2053 // phone links within ajax-updated truck search list items 2054 var selector = ".truck__phone-number .truck__phone-link"; 2055 rebindClickToCallEvent(selector); 2056 2057 wts.ready.then(function (wts, modules) { 2058 if (wts.truckSearch) { 2059 2060 wts.truckSearch.on("rebind", function () { 2061 rebindClickToCallEvent(selector); 2062 }); 2063 } 2064 }); 2065 2066 // phone links within the vdp pricing column (delegated â pricing tel link is injected after ready) 2067 $(document).on( 2068 "click", 2069 ".mobile-call-action-link .action-link, .advanced-pricing-actions__phone-link, .vehicle-details__comvoy-mobile-action-link-phone", 2070 function (e) { 2071 var fordDirectEnabled = 2072 modules.FordDirectAnalytics && 2073 modules.FordDirectAnalytics.isEnabled(); 2074 2075 if ( 2076 !wts.user.isTruckProOrAbove && 2077 (window.analyticsEnabled || fordDirectEnabled) 2078 ) { 2079 var $t = $(this); 2080 var isPhotoGalleryPhone = 2081 $t.closest("#photo-gallery-modal").length > 0; 2082 var invID = isPhotoGalleryPhone 2083 ? $t 2084 .closest("#photo-gallery-modal") 2085 .find(".im-interested-container") 2086 .attr("data-truck-id") || 2087 wts.sender.getInventory().current 2088 : wts.sender.getInventory().current; 2089 var hrefTel = ($t.attr("href") || "").replace(/^tel:/i, "").trim(); 2090 var e164 = 2091 modules.utilities.phoneToE164(hrefTel) || 2092 modules.utilities.phoneToE164($t.attr("data-comm-phone-number")) || 2093 modules.utilities.phoneToE164($t.text().trim()); 2094 var display = 2095 $t.attr("data-phonedisplay") || 2096 $t.closest(".phone-holder-pricing__phone-link").attr("data-phonedisplay") || 2097 $t.text().trim(); 2098 2099 var gaLabel = 2100 (display && modules.utilities.phoneToE164(display)) || 2101 display || 2102 e164 || 2103 ""; 2104 2105 // fire and wait call us now event 2106 wts.visor.fireAndWait(UI_EVENTS.CALL_US_NOW_IM_INTERESTED, { 2107 inventoryID: invID, 2108 commPhoneNumber: e164 || undefined, 2109 gaPhoneLabel: gaLabel || undefined, 2110 modalContext: isPhotoGalleryPhone 2111 ? "see-all-photos-i-am-interested" 2112 : undefined, 2113 clickToCallSource: isPhotoGalleryPhone 2114 ? undefined 2115 : "vehicle-details", 2116 }); 2117 } 2118 } 2119 ); 2120 });
2121 2122 /** 2123 * Rebinds our truck search listing `Phone Call` link event listeners. 2124 * @param {string} selector the class name or id of the click-to-call HTMLElement. 2125 * @returns {void} 2126 */ 2127 function rebindClickToCallEvent(selector) { 2128 $(selector).off("click").click(function (e) { 2129 var invID = $(this).closest(".truck-listings__item").attr("data-truck-id"); 2130 // fire and wait call us now event 2131 // trackClickToCallEvent("Search", "Phone Call", invID); 2132 wts.visor.fireAndWait(UI_EVENTS.CALL_US_NOW_IM_INTERESTED, 2133 { 2134 category: "Search", 2135 action: "Phone Call", 2136 inventoryID: invID 2137 } 2138 ); 2139 }); 2140 } 2141 2142})(window.wts, window.wts.modules); 2143; 2144(function (wts, modules) { 2145 // imports 2146 getClientData = modules.getClientData; 2147 2148 // initialization of module 2149 var ascTrackingSetup = getClientData("#asc-tracking-setup"); 2150 2151 // "private static" members 2152 var _vocationalCarouselCurrentPage = 0; 2153 var _formInteractions = new Map(); 2154 var _customFormInteractions = new Map(); 2155 var _enableAscTracking = ascTrackingSetup.enableAscTracking; 2156 var _ascMeasurementIDs = ascTrackingSetup.ascMeasurementIDs; 2157 var _enableDebug = ascTrackingSetup.enableDebug; 2158 let _isResizing = false; 2159 let _resizeTimer; 2160 2161 /** 2162 * @param {JQuery} $element clicked phone link 2163 * @returns {string} E.164 when parsable; otherwise best-effort visible/href text 2164 */ 2165 function normalizeCommPhoneFromClick($element) { 2166 var hrefRaw = ($element.attr("href") || "").replace(/^tel:/i, "").trim(); 2167 var fromHref = modules.utilities.phoneToE164(hrefRaw); 2168 if (fromHref) { 2169 return fromHref; 2170 } 2171 var dataComm = $element.attr("data-comm-phone-number"); 2172 if (dataComm) { 2173 var fromData = modules.utilities.phoneToE164(dataComm); 2174 if (fromData) { 2175 return fromData; 2176 } 2177 } 2178 var text = modules.utilities.getInnerText($element); 2179 return modules.utilities.phoneToE164(text) || text || ""; 2180 } 2181 2182 /** 2183 * ASC `element_text` for click-to-call: E.164 when the visible label parses as a phone; 2184 * otherwise keep human-readable text; if empty, fall back to normalized {@link normalizeCommPhoneFromClick}. 2185 * @param {JQuery} $element clicked phone link 2186 * @param {string} commPhoneFallback E.164 or best-effort value from {@link normalizeCommPhoneFromClick} 2187 */ 2188 function commPhoneElementText($element, commPhoneFallback) { 2189 var fromAttr = $element.attr("data-phonedisplay"); 2190 var inner = modules.utilities.getInnerText($element); 2191 var candidate = 2192 (fromAttr && String(fromAttr).trim()) || 2193 (inner && String(inner).trim()) || 2194 ""; 2195 2196 var asE164 = modules.utilities.phoneToE164(candidate); 2197 if (asE164) { 2198 return asE164; 2199 } 2200 if (candidate) { 2201 return candidate; 2202 } 2203 var fb = (commPhoneFallback && String(commPhoneFallback).trim()) || ""; 2204 return modules.utilities.phoneToE164(fb) || fb; 2205 } 2206 2207 // Set up resize event listeners to manage the _isResizing flag 2208 window.addEventListener("resize", function () { 2209 _isResizing = true; 2210 clearTimeout(_resizeTimer); 2211 _resizeTimer = setTimeout(function () { 2212 _isResizing = false; 2213 }, 250); // 250ms delay to ensure resize event has settled 2214 }); 2215 2216 const EVENT = Object.fromEntries( 2217 [ 2218 "asc_pageview", 2219 "asc_item_pageview", 2220 "asc_itemlist_pageview", 2221 "asc_retail_process", 2222 "asc_cta_interaction", 2223 "asc_element_configuration", 2224 "asc_menu_interaction", 2225 "asc_media_interaction", 2226 "asc_special_offer", 2227 "asc_click_to_call", 2228 "asc_form_engagement", 2229 "asc_form_submission", 2230 "asc_form_submission_sales", 2231 "asc_form_submission_parts", 2232 "asc_form_submission_service", 2233 "asc_form_submission_appt", 2234 "asc_form_submission_other", 2235 ].map((value) => [value.toUpperCase(), value]) 2236 ); 2237 2238 const COMM_OUTCOME = Object.fromEntries( 2239 [ 2240 "appt_booked", 2241 "vehicle_ordered", 2242 "parts_ordered", 2243 "get_back_commitment", 2244 "not_opportunity", 2245 "robocall", 2246 "opportunity", 2247 "unknown", 2248 ].map((value) => [value.toUpperCase(), value]) 2249 ); 2250 2251 const COMM_STATUS = Object.fromEntries( 2252 [ 2253 "start", 2254 "close", 2255 "engage", 2256 "inject", 2257 "send_deal", 2258 "send_payment", 2259 "send_appt", 2260 "idle", 2261 "timeout", 2262 "unanswered", 2263 "transfer_abandon", 2264 "hold_time_abandon", 2265 "voicemail", 2266 "answered", 2267 "crm_update", 2268 "appt_booked", 2269 "phone_tree_keypress", 2270 "send", 2271 "agent_escalation", 2272 "digital_retailing_handoff", 2273 "load", 2274 "video_start", 2275 "video_end", 2276 "connect", 2277 "ring", 2278 ].map((value) => [value.toUpperCase(), value]) 2279 ); 2280 2281 const COMM_TYPE = Object.fromEntries( 2282 ["chat", "sms", "voice", "video", "form", "email"].map((value) => [ 2283 value.toUpperCase(), 2284 value, 2285 ]) 2286 ); 2287 2288 const DEPARTMENT = Object.fromEntries( 2289 [ 2290 "sales", 2291 "service", 2292 "parts", 2293 "trade", 2294 "credit", 2295 "body_shop", 2296 "admin", 2297 "accessories", 2298 "hr", 2299 "custom_shop", 2300 "rentals", 2301 "unknown", 2302 "quick_lube", 2303 ].map((value) => [value.toUpperCase(), value]) 2304 ); 2305 2306 const ELEMENT_POSITION = Object.fromEntries( 2307 [ 2308 "top_left", 2309 "top_right", 2310 "top_center", 2311 "center_left", 2312 "center_right", 2313 "center_center", 2314 "bottom_left", 2315 "bottom_right", 2316 "bottom_center", 2317 "floating", 2318 "inline", 2319 "unknown", 2320 ].map((value) => [value.toUpperCase(), value]) 2321 ); 2322 2323 const ELEMENT_STATE = Object.fromEntries( 2324 ["active", "inactive"].map((value) => [value.toUpperCase(), value]) 2325 ); 2326 2327 const ELEMENT_TYPE = Object.fromEntries( 2328 [ 2329 "header", 2330 "banner", 2331 "popup", 2332 "form", 2333 "digital_retailing_tool", 2334 "trade_tool", 2335 "chat_tool", 2336 "accessability_tool", 2337 "search_tool", 2338 "scheduling_tool", 2339 "gallery", 2340 "map", 2341 "footer", 2342 "video", 2343 "item_details", 2344 "pagination", 2345 "body", 2346 "menu", 2347 ].map((value) => [value.toUpperCase(), value]) 2348 ); 2349 2350 const ELEMENT_SUBTYPE = Object.fromEntries( 2351 [ 2352 "cta_button", 2353 "cta_icon", 2354 "nav_button", 2355 "nav_icon", 2356 "nav_tab", 2357 "hamburger_menu", 2358 "mega_menu", 2359 "sidebar_menu", 2360 "inline_menu", 2361 "hyperlink", 2362 "content", 2363 "share_icon", 2364 "image", 2365 "input_field", 2366 "toggle", 2367 "dropdown", 2368 "kebab_menu", 2369 "hero_image", 2370 ].map((value) => [value.toUpperCase(), value]) 2371 ); 2372 2373 const EVENT_ACTION = Object.fromEntries( 2374 [ 2375 "click", 2376 "swipe_left", 2377 "swipe_right", 2378 "swipe_up", 2379 "swipe_down", 2380 "hover", 2381 "view", 2382 "scroll", 2383 "field_input", 2384 "keystroke", 2385 ].map((value) => [value.toUpperCase(), value]) 2386 ); 2387 2388 const EVENT_ACTION_RESULT = Object.fromEntries( 2389 [ 2390 "play", 2391 "restart", 2392 "vr_play", 2393 "spin", 2394 "popup", 2395 "slideout", 2396 "exit", 2397 "error", 2398 "search", 2399 "calc", 2400 "start", 2401 "stop", 2402 "redirect", 2403 "open", 2404 "close", 2405 "increase", 2406 "decrease", 2407 "clear", 2408 "check", 2409 "uncheck", 2410 "complete",
2411 "dropdown", 2412 "none", 2413 "shown", 2414 "next_step", 2415 ].map((value) => [value.toUpperCase(), value]) 2416 ); 2417 2418 const FORM_TYPE = Object.fromEntries( 2419 [ 2420 "consumer_contact", 2421 "trade", 2422 "recall", 2423 "parts", 2424 "accessories", 2425 "service_appointment", 2426 "pre-order", 2427 "custom", 2428 "bodyshop", 2429 "finance/credit", 2430 "human_resources", 2431 "research", 2432 "surveys", 2433 "unknown", 2434 "alert", 2435 "offer", 2436 "quote", 2437 "download", 2438 "shipment", 2439 "purchase", 2440 ].map((value) => [value.toUpperCase(), value]) 2441 ); 2442 2443 const MEDIA_TYPE = Object.fromEntries( 2444 [ 2445 "video", 2446 "image", 2447 "vr", 2448 "slider", 2449 "gallery", 2450 "offer", 2451 "360_platform", 2452 "map", 2453 "thumbnail", 2454 ].map((value) => [value.toUpperCase(), value]) 2455 ); 2456 2457 const PAGE_TYPE = Object.fromEntries( 2458 [ 2459 "item", 2460 "itemlist", 2461 "contact", 2462 "directions", 2463 "service", 2464 "parts", 2465 "accessories", 2466 "body_shop", 2467 "home", 2468 "build_to_order", 2469 "custom", 2470 "complaint", 2471 "employment", 2472 "recall", 2473 "finance", 2474 "credit", 2475 "trade", 2476 "commercial", 2477 "showroom", 2478 "unknown", 2479 "thank_you", 2480 "specials", 2481 "custom_orders", 2482 ].map((value) => [value.toUpperCase(), value]) 2483 ); 2484 2485 const ASC_MAPPINGS = { 2486 EVENT, 2487 COMM_OUTCOME, 2488 COMM_STATUS, 2489 COMM_TYPE, 2490 DEPARTMENT, 2491 ELEMENT_POSITION, 2492 ELEMENT_STATE, 2493 ELEMENT_TYPE, 2494 ELEMENT_SUBTYPE, 2495 EVENT_ACTION, 2496 EVENT_ACTION_RESULT, 2497 FORM_TYPE, 2498 MEDIA_TYPE, 2499 PAGE_TYPE, 2500 }; 2501 2502 const REQUIRED_PARAMS = { 2503 asc_pageview: ["event_owner", "page_type"], 2504 asc_retail_process: [ 2505 "event_owner", 2506 "page_type", 2507 "flow_name", 2508 "element_title", 2509 "flow_outcome", 2510 ], 2511 asc_cta_interaction: [ 2512 "event_owner", 2513 "page_type", 2514 "element_text", 2515 "element_type", 2516 "element_subtype", 2517 "event_action", 2518 "event_action_result", 2519 "link_url", 2520 ], 2521 asc_element_configuration: [ 2522 "event_owner", 2523 "page_type", 2524 "event_action_result", 2525 "event_action", 2526 "element_state", 2527 "element_type", 2528 "element_subtype", 2529 "element_title", 2530 "element_text", 2531 "element_value", 2532 ], 2533 asc_menu_interaction: [ 2534 "event_owner", 2535 "page_type", 2536 "element_text", 2537 "element_subtype", 2538 "element_type", 2539 "event_action", 2540 ], 2541 asc_media_interaction: [ 2542 "event_owner", 2543 "page_type", 2544 "event_action", 2545 "event_action_result", 2546 "media_type", 2547 "link_url", 2548 ], 2549 asc_special_offer: ["event_owner", "page_type", "promotion_name"], 2550 asc_click_to_call: [ 2551 "event_owner", 2552 "page_type", 2553 "comm_phone_number", 2554 "element_text", 2555 "department", 2556 ], 2557 asc_form_engagement: [ 2558 "event_owner", 2559 "page_type", 2560 "comm_type", 2561 "comm_status", 2562 "element_text", 2563 "department", 2564 "form_name",
2565 "form_type", 2566 ], 2567 asc_form_submission: [ 2568 "event_owner", 2569 "page_type", 2570 "comm_type", 2571 "comm_outcome", 2572 "submission_id", 2573 "element_text", 2574 "department", 2575 "form_name", 2576 "form_type", 2577 ], 2578 asc_form_submission_sales: [ 2579 "event_owner", 2580 "page_type", 2581 "comm_type", 2582 "comm_outcome", 2583 "submission_id", 2584 "element_text", 2585 "department", 2586 "form_name", 2587 "form_type", 2588 ], 2589 asc_form_submission_parts: [ 2590 "event_owner", 2591 "page_type", 2592 "comm_type", 2593 "comm_outcome", 2594 "submission_id", 2595 "element_text", 2596 "department", 2597 "form_name", 2598 "form_type", 2599 ], 2600 asc_form_submission_service: [ 2601 "event_owner", 2602 "page_type", 2603 "comm_type", 2604 "comm_outcome", 2605 "submission_id", 2606 "element_text", 2607 "department", 2608 "form_name",
2609 "form_type", 2610 ], 2611 asc_form_submission_appt: [ 2612 "event_owner", 2613 "page_type", 2614 "comm_type", 2615 "comm_outcome", 2616 "submission_id", 2617 "element_text", 2618 "department", 2619 "form_name", 2620 "form_type", 2621 ], 2622 asc_form_submission_other: [ 2623 "event_owner", 2624 "page_type", 2625 "comm_type", 2626 "comm_outcome", 2627 "submission_id", 2628 "element_text", 2629 "department", 2630 "form_name", 2631 "form_type", 2632 ], 2633 }; 2634 2635 const OPTIONAL_PARAMS = { 2636 asc_pageview: [ 2637 "product_name", 2638 "item_id", 2639 "item_number", 2640 "item_price", 2641 "item_condition", 2642 "item_year", 2643 "item_make", 2644 "item_model", 2645 "item_variant", 2646 "item_color", 2647 "item_type", 2648 "item_category", 2649 "item_inventory_date", 2650 "item_results", 2651 "item_location", 2652 ], 2653 asc_retail_process: [ 2654 "flow_stage", 2655 "product_name", 2656 "item_payment", 2657 "item_id", 2658 "item_number", 2659 "item_price", 2660 "item_condition", 2661 "item_year", 2662 "item_make", 2663 "item_model", 2664 "item_variant", 2665 "item_color", 2666 "item_type", 2667 "item_category", 2668 "item_inventory_date", 2669 "item_location", 2670 ], 2671 asc_cta_interaction: [ 2672 "element_color", 2673 "element_order", 2674 "product_name", 2675 "affiliation", 2676 "item_id", 2677 "item_number", 2678 "item_price", 2679 "item_condition", 2680 "item_year", 2681 "item_make", 2682 "item_model", 2683 "item_variant", 2684 "item_color", 2685 "item_type", 2686 "item_category", 2687 "item_inventory_date", 2688 "item_location", 2689 ], 2690 asc_element_configuration: [ 2691 "product_name", 2692 "element_color", 2693 "element_position", 2694 "element_order", 2695 ], 2696 asc_menu_interaction: ["product_name"], 2697 asc_media_interaction: [ 2698 "affiliation", 2699 "creative_name", 2700 "product_name", 2701 "item_id", 2702 "item_number", 2703 "item_price", 2704 "item_condition", 2705 "item_year", 2706 "item_make", 2707 "item_model", 2708 "item_variant", 2709 "item_color", 2710 "item_type", 2711 "item_category", 2712 "item_inventory_date", 2713 "item_location", 2714 ], 2715 asc_special_offer: [ 2716 "department", 2717 "product_name", 2718 "element_position", 2719 "flow_outcome", 2720 ], 2721 asc_click_to_call: [ 2722 "affiliation", 2723 "product_name", 2724 "item_id", 2725 "item_number", 2726 "item_price", 2727 "item_condition", 2728 "item_year", 2729 "item_make", 2730 "item_model", 2731 "item_variant", 2732 "item_color", 2733 "item_type", 2734 "item_category", 2735 "item_inventory_date", 2736 "item_location", 2737 ], 2738 asc_form_engagement: [ 2739 "product_name", 2740 "item_id", 2741 "item_number", 2742 "item_price", 2743 "item_condition", 2744 "item_year", 2745 "item_make", 2746 "item_model", 2747 "item_variant", 2748 "item_color", 2749 "item_type", 2750 "item_category", 2751 "item_inventory_date", 2752 "item_location", 2753 ], 2754 asc_form_submission: [ 2755 "product_name", 2756 "item_id", 2757 "item_number", 2758 "item_price", 2759 "item_condition", 2760 "item_year", 2761 "item_make", 2762 "item_model", 2763 "item_variant", 2764 "item_color", 2765 "item_type", 2766 "item_category", 2767 "item_inventory_date", 2768 "item_location", 2769 ], 2770 }; 2771 2772 // "public" functions 2773 AscTracking = { 2774 /** 2775 * Resets window.asc_datalayer.items to an empty array. 2776 */ 2777 resetItems: function () { 2778 if (!_enableAscTracking) { 2779 return; 2780 } 2781 2782 window.asc_datalayer.items = []; 2783 }, 2784 2785 /** 2786 * Adds the specified vehicle information to the window.asc_datalayer.items array. 2787 * @param {any} color The color of the vehicle. 2788 * @param {any} condition The condition of the vehicle. 2789 * @param {any} fuelType The fuel type of the vehicle. 2790 * @param {any} vin The vin of the vehicle. 2791 * @param {any} createdDate The date the vehicle was added to inventory. 2792 * @param {any} make The make of the vehicle. 2793 * @param {any} model The model of the vehicle. 2794 * @param {any} stockNumber The stock number of the vehicle. 2795 * @param {any} bodyStyle The body style of the vehicle. 2796 * @param {any} trim The trim of the vehicle. 2797 * @param {any} year The year of the vehicle. 2798 * @param {any} price The price of the vehicle. 2799 * @param {any} inventoryID The Inventory ID of the vehicle. 2800 */
2801 addVehicleToAscDatalayerItems: function ( 2802 color, 2803 condition, 2804 fuelType, 2805 vin, 2806 createdDate, 2807 make, 2808 model, 2809 stockNumber, 2810 bodyStyle, 2811 trim, 2812 year, 2813 price, 2814 inventoryID, 2815 location 2816 ) { 2817 if (!_enableAscTracking) { 2818 return; 2819 } 2820 2821 window.asc_datalayer.items.push({ 2822 item_category: "", // TODO: implement category - doc has "domestic" so we need a way of telling either foreign or domestic, example given shows the empty string 2823 item_color: color, 2824 item_condition: condition, 2825 item_fuel_type: fuelType, 2826 item_id: vin, 2827 item_inventory_date: createdDate, 2828 item_make: make, 2829 item_model: model, 2830 item_number: stockNumber, 2831 item_payment: "finance", // TODO: implement other options - doc states "Payment shown, or selected which is associated with the event", example given shows "finance" 2832 item_type: bodyStyle, 2833 item_variant: trim, 2834 item_year: year, 2835 item_price: price, 2836 item_inventory_id: inventoryID, 2837 item_location: location, 2838 }); 2839 }, 2840 2841 /** 2842 * Tracks a page view event. 2843 * @param {any} event Event data giving context to the page view event. 2844 */ 2845 trackPageViewEvent: function (event) { 2846 if (!_enableAscTracking) { 2847 return; 2848 } 2849 2850 var ascEventData = { 2851 event: event.type, 2852 event_owner: window.asc_datalayer.affiliation, 2853 page_type: 2854 window.asc_datalayer.page_type === "custom_page" 2855 ? "custom" 2856 : window.asc_datalayer.page_type, 2857 }; 2858 2859 var errorCode = window.asc_datalayer.error_code; 2860 if ( 2861 typeof errorCode === "number" && 2862 errorCode >= 400 2863 ) { 2864 ascEventData.error_code = errorCode; 2865 } else if ( 2866 typeof errorCode === "string" && 2867 errorCode !== "none" && 2868 !isNaN(Number(errorCode)) && 2869 Number(errorCode) >= 400 2870 ) { 2871 ascEventData.error_code = Number(errorCode); 2872 } 2873 2874 var items = window.asc_datalayer.items || []; 2875 if (items.length > 0) { 2876 Object.assign(ascEventData, formatData(items)); 2877 var paginationCount = Number( 2878 $(".pagination__counts em:nth-child(3)").first().text() 2879 ); 2880 if (!isNaN(paginationCount) && paginationCount > 0) { 2881 ascEventData.item_results = paginationCount; 2882 } else { 2883 ascEventData.item_results = items.length; 2884 } 2885 } 2886 2887 trackAscEvent(ascEventData); 2888 }, 2889 2890 /** 2891 * Tracks a general layout interaction click event that occurs when various controls from the general layout of the UI are clicked. 2892 * @param {any} event Event data giving context to the general layout click event. 2893 */ 2894 fireAscEvent: function (ascEventData) { 2895 trackAscEvent(ascEventData); 2896 }, 2897 2898 trackGeneralLayoutInteractionClick: function (event) { 2899 if (!_enableAscTracking) { 2900 return; 2901 } 2902 2903 if ( 2904 modules.AscDelegatedTracking && 2905 modules.AscDelegatedTracking.trackDecoratedClick( 2906 event.detail 2907 ) 2908 ) { 2909 return; 2910 } 2911 2912 // helper function used to find the matching item in window.asc_datalayer.items 2913 const getMatchingItem = function ($el) { 2914 const truckIDTarget = 2915 $el.data("truckId") || modules.utilities.getTruckID($el); 2916 const marketingData = truckIDTarget 2917 ? modules.getClientData(
2918 `#analytics-marketing-data-${truckIDTarget}` 2919 ) // get data from the specific #analytics-marketing-data- section (VSP or I'm Interested Form) 2920 : modules.getClientData(`#marketing-events-data`); // get data from the #marketing-events-data section (VDP) 2921 return marketingData 2922 ? [ 2923 window.asc_datalayer.items.find( 2924 (i) => i.item_id === marketingData.vin 2925 ), 2926 ] 2927 : []; 2928 }; 2929 2930 const currentTarget = event.detail; 2931 const $element = $(currentTarget); 2932 const elementText = modules.utilities.getInnerText($element); 2933 2934 var ascEventData = { 2935 // Required for CTA events 2936 event: event.type, 2937 page_type: window.asc_datalayer.page_type, 2938 event_owner: window.asc_datalayer.affiliation, 2939 element_text: elementText, 2940 element_title: $element.data("asc-element-title") || "", 2941 element_type: 2942 event?.detail.elementType || ELEMENT_TYPE.ITEM_DETAILS, 2943 element_subtype: 2944 event?.detail.elementSubtype || ELEMENT_SUBTYPE.NAV_BUTTON, 2945 event_action: event?.detail.eventAction || EVENT_ACTION.CLICK, 2946 event_action_result: 2947 event?.detail.eventActionResult || EVENT_ACTION_RESULT.OPEN, 2948 link_url: event?.detail.linkUrl, 2949 // Optional 2950 items: window.asc_datalayer.items, 2951 }; 2952 2953 if (currentTarget.href) { 2954 // handle all links: i.e. link back to dealer site, links to the VDP, or links in the footer 2955 2956 // test to see if this is a navigation link which is different than the links within the inventory grid 2957 var parentElement = null; 2958 var testElement = $element.parent(); 2959 const elementOptions = [ 2960 "navigation__link", 2961 "mobile-auth-link", 2962 "return-home-link", 2963 "breadcrumb", 2964 "footer-nav", 2965 ]; 2966 2967 for (option of elementOptions) { 2968 if (testElement.hasClass(option)) { 2969 parentElement = testElement; 2970 break; 2971 } 2972 } 2973 2974 if (!parentElement) { 2975 if ($element.parents(".footer-nav").length) { 2976 parentElement = $element.parents(".footer-nav").first(); 2977 } 2978 } 2979 2980 if (parentElement && parentElement.attr("class")) { 2981 // header/footer link 2982 ascEventData.element_type = parentElement.hasClass( 2983 "footer-nav" 2984 ) 2985 ? "footer" 2986 : "header"; 2987 ascEventData.element_text = 2988 modules.utilities.getInnerText($element) || 2989 $element[0].innerText; 2990 2991 // Set element_title to parent dropdown text in header dropdown menus 2992 if (ascEventData.element_type === "header") { 2993 const dropdownLinkText = 2994 $element 2995 .closest(".dropdown") 2996 .find(".dropdown__link") 2997 .text() 2998 .trim() || ""; 2999 ascEventData.element_title = dropdownLinkText; 3000 } 3001 3002 // Truck Pro Login button 3003 if (ascEventData.element_text === "") { 3004 if ($element.hasClass("auth--login")) { 3005 ascEventData.element_text = 3006 modules.utilities.getInnerText( 3007 $element 3008 .children(".log-in-out-text") 3009 .first() 3010 ); 3011 } 3012 } 3013 3014 // Custom orders page type 3015 if ( 3016 ascEventData.element_text.toLowerCase() === 3017 "custom orders" 3018 ) { 3019 ascEventData.page_type = PAGE_TYPE.CUSTOM; 3020 } 3021 3022 ascEventData.event = EVENT.ASC_MENU_INTERACTION; 3023 } else if ($element.hasClass("specifications__see-more")) { 3024 // handles see more details link click to open the VDP from VSP 3025 ascEventData.items = getMatchingItem($element); 3026 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3027 ascEventData.link_url = $element[0].href; 3028 }
3028 else if ($element.is(".js-custom-orders__asc-event")) { 3029 // handles see more body type details link on Custom Orders 3030 ascEventData.element_text = 3031 $element[0].innerText || ascEventData.element_title; 3032 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3033 ascEventData.link_url = $element[0].href; 3034 } else if ($element.hasClass("truck-image")) { 3035 // handles truck image click to open the VDP from VSP 3036 ascEventData.event = EVENT.ASC_MEDIA_INTERACTION; 3037 ascEventData.media_type = MEDIA_TYPE.IMAGE; 3038 ascEventData.link_url = $element 3039 .find(".truck-image__image") 3040 .attr("src"); 3041 ascEventData.items = getMatchingItem($element); 3042 } else if ($element.hasClass("truck__link-text")) { 3043 // handles truck title click to open the VDP from VSP 3044 ascEventData.items = getMatchingItem($element); 3045 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3046 ascEventData.link_url = $element[0].href; 3047 } else if ($element.hasClass("truck__phone-link")) { 3048 // handles phone number click within the inventory grid item 3049 ascEventData.event = EVENT.ASC_CLICK_TO_CALL; 3050 ascEventData.comm_phone_number = 3051 normalizeCommPhoneFromClick($element); 3052 ascEventData.element_text = commPhoneElementText( 3053 $element, 3054 ascEventData.comm_phone_number 3055 ); 3056 ascEventData.department = DEPARTMENT.SALES; 3057 ascEventData.affiliation = window.asc_datalayer.affiliation; 3058 ascEventData.items = getMatchingItem($element); 3059 } else if ($element.hasClass("call-us-now-phone")) { 3060 // handles phone number click at bottom of I'm Interested form 3061 ascEventData.event = EVENT.ASC_CLICK_TO_CALL; 3062 ascEventData.comm_phone_number = 3063 normalizeCommPhoneFromClick($element); 3064 ascEventData.element_text = commPhoneElementText( 3065 $element, 3066 ascEventData.comm_phone_number 3067 ); 3068 ascEventData.department = DEPARTMENT.SALES; 3069 ascEventData.affiliation = window.asc_datalayer.affiliation; 3070 ascEventData.items = getMatchingItem($element); 3071 } else if ( 3072 $element.hasClass("advanced-pricing-actions__phone-link") 3073 ) { 3074 // handles phone number click directly on VDP 3075 ascEventData.event = EVENT.ASC_CLICK_TO_CALL; 3076 ascEventData.event_action = EVENT_ACTION.CLICK; 3077 ascEventData.comm_phone_number = 3078 normalizeCommPhoneFromClick($element); 3079 ascEventData.element_text = commPhoneElementText( 3080 $element, 3081 ascEventData.comm_phone_number 3082 ); 3083 ascEventData.department = DEPARTMENT.SALES; 3084 ascEventData.affiliation = window.asc_datalayer.affiliation; 3085 ascEventData.items = getMatchingItem($element); 3086 } else if ($element.hasClass("photo-gallery__open-image")) { 3087 // handles open click from photo gallery 3088 ascEventData.event = EVENT.ASC_MEDIA_INTERACTION; 3089 ascEventData.media_type = MEDIA_TYPE.IMAGE; 3090 ascEventData.link_url = currentTarget.href; 3091 ascEventData.items = getMatchingItem($element); 3092 } else if ( 3093 $element.hasClass("wts-vehicle-carouse__lite-item-link") 3094 ) { 3095 // handles vehicle click from vehicle carousel: either recently viewed or similar vehicles 3096 ascEventData.event = EVENT.ASC_MEDIA_INTERACTION; 3097 ascEventData.element_text = $element.attr("aria-label"); 3098 ascEventData.media_type = MEDIA_TYPE.IMAGE; 3099 ascEventData.link_url = $element[0].href; 3100 ascEventData.items = getMatchingItem($element); 3101 } else if ($element.hasClass("phone-link")) { 3102 // handles generic phone number click 3103 ascEventData.event = EVENT.ASC_CLICK_TO_CALL; 3104 ascEventData.comm_phone_number = 3105 normalizeCommPhoneFromClick($element); 3106 ascEventData.element_text = commPhoneElementText( 3107 $element, 3108 ascEventData.comm_phone_number 3109 ); 3110 ascEventData.department = DEPARTMENT.SALES; 3111 ascEventData.affiliation = $element 3112 .parent() 3113 .siblings(".crestline-logo") 3114 ? "crestline" 3115 : window.asc_datalayer.affiliation; 3116 } else if ($element.hasClass("return-home-link")) { 3117 // handles the return home link above the nav bar 3118 ascEventData.items = getMatchingItem($element); 3119 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3120 ascEventData.link_url = $element[0].href; 3121 } else if ($element[0].href.endsWith("/VanBuilder")) { 3122 // handles van builder click 3123 ascEventData.event = EVENT.ASC_MEDIA_INTERACTION; 3124 ascEventData.media_type = MEDIA_TYPE.IMAGE; 3125 ascEventData.link_url = $element[0].href; 3126 } else if ( 3127 $element.hasClass("additional-resources__dealer-video-link") 3128 ) { 3129 // handles VDP view dealer video click 3130 ascEventData.element_text = $element[0].innerText; 3131 ascEventData.element_type = ELEMENT_TYPE.VIDEO; 3132 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3133 ascEventData.link_url = ""; 3134 } else if ($element.hasClass("resource")) {
3135 // handles VDP view window sticker click 3136 ascEventData.element_text = 3137 $element.children(".resource-text")[0].innerText; 3138 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3139 ascEventData.link_url = $element[0].href; 3140 } else if ($element.hasClass("dynamic-link")) { 3141 // handles VDP dynamic link clicks 3142 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3143 ascEventData.element_text = $element[0].textContent; 3144 ascEventData.event_action_result = 3145 EVENT_ACTION_RESULT.SEARCH; 3146 ascEventData.link_url = $element[0].href; 3147 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3148 } else if ($element.hasClass("toggle-link")) { 3149 ascEventData.element_text = $element[0].innerText; 3150 ascEventData.element_type = ELEMENT_TYPE.ITEM_DETAILS; 3151 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3152 ascEventData.link_url = ""; 3153 } else if ( 3154 $element.attr("id") === "view-vehicle-options-link" 3155 ) { 3156 ascEventData.element_text = $element[0].innerText; 3157 ascEventData.element_type = ELEMENT_TYPE.ITEM_DETAILS; 3158 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3159 ascEventData.link_url = ""; 3160 } else if ($element.parent(".social").length) { 3161 // handles social link clicks in footer 3162 ascEventData.element_type = ELEMENT_TYPE.FOOTER; 3163 ascEventData.element_text = $element.attr("aria-label"); 3164 ascEventData.event_action_result = EVENT_ACTION_RESULT.OPEN; 3165 ascEventData.link_url = $element[0].href; 3166 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3167 } else if ($element.hasClass("ez-order-button--trigger")) { 3168 // handles EZOrder modal launch events when the trigger is an 'a' tag 3169 ascEventData.element_type = ELEMENT_TYPE.POPUP; 3170 ascEventData.event_action_result = EVENT_ACTION_RESULT.OPEN; 3171 ascEventData.link_url = $element[0].href; 3172 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3173 } else if ($element.hasClass("back-top")) { 3174 // handles back to top click 3175 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3176 ascEventData.element_text = $element[0].textContent.trim(); 3177 ascEventData.event_action_result = 3178 EVENT_ACTION_RESULT.SEARCH; 3179 ascEventData.link_url = $element[0].href; 3180 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3181 } else if ($element.hasClass("learn-more-link")) { 3182 // handle Crestline button click 3183 ascEventData.element_type = 3184 ELEMENT_TYPE.DIGITAL_RETAILING_TOOL; 3185 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3186 ascEventData.link_url = $element[0].href; 3187 } else if ($element.parent(".definition-primary-nav").length) { 3188 // handles definitions A-Z nav bar click 3189 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3190 ascEventData.element_text = $element[0].textContent.trim(); 3191 ascEventData.event_action_result = 3192 EVENT_ACTION_RESULT.SEARCH; 3193 ascEventData.link_url = $element[0].href; 3194 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3195 } else if ( 3196 $element.parents(".footer-static-links__list").length 3197 ) { 3198 // handles static footer links click 3199 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3200 ascEventData.element_text = $element[0].textContent.trim(); 3201 ascEventData.event_action_result = 3202 EVENT_ACTION_RESULT.SEARCH; 3203 ascEventData.link_url = $element[0].href; 3204 ascEventData.element_subtype = ELEMENT_SUBTYPE.HYPERLINK; 3205 } else if ($element.hasClass("share-email")) { 3206 ascEventData.element_text = $element[0].textContent.trim(); 3207 ascEventData.element_title = 3208 $element.data("asc-element-title"); 3209 } else { 3210 // check if it is a link in a custom header (typically a "Back to somewhere.com" link) 3211 let parentDiv; 3212 3213 if ( 3214 $element.parents(".custom-header").length || 3215 $element.parents(".custom-secondary-header").length || 3216 $element.parents(".header-info").length 3217 ) { 3218 parentDiv = 3219 $element.parents(".custom-header")[0] || 3220 $element.parents(".custom-secondary-header")[0] || 3221 $element.parents(".header-info")[0]; 3222 } 3223 3224 if (parentDiv) { 3225 ascEventData.element_type = ELEMENT_TYPE.HEADER;
3226 ascEventData.element_text = 3227 modules.utilities.getInnerText($element); 3228 ascEventData.event_action_result = 3229 EVENT_ACTION_RESULT.EXIT; 3230 ascEventData.link_url = currentTarget.href; 3231 } else { 3232 // presently an unhandled event so return which means don't fire an event 3233 return; 3234 } 3235 } 3236 } else if ($element.hasClass("facet-range__input")) { 3237 var facetGroupText = ( 3238 $element 3239 .parents(".facet-filters") 3240 .first() 3241 .children("h4")[0] || 3242 $element 3243 .parents(".facets-theme.facet-filters") 3244 .children(".facet-filters__heading")[0] 3245 ).innerText.trim(); 3246 3247 // handle facet range changes (i.e. Year, Cab to Axle, GVRW) 3248 ascEventData.event = EVENT.ASC_ELEMENT_CONFIGURATION; 3249 ascEventData.event_action_result = EVENT_ACTION_RESULT.SEARCH; 3250 ascEventData.event_action = EVENT_ACTION.FIELD_INPUT; 3251 ascEventData.element_text = $element.val(); 3252 ascEventData.element_title = facetGroupText; 3253 ascEventData.element_value = ascEventData.element_text; 3254 ascEventData.element_state = ascEventData.element_text 3255 ? ELEMENT_STATE.ACTIVE 3256 : ELEMENT_STATE.INACTIVE; 3257 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3258 ascEventData.element_subtype = $element.parent( 3259 "div.facet-range__control.combobox" 3260 ).length 3261 ? ELEMENT_SUBTYPE.DROPDOWN 3262 : ELEMENT_SUBTYPE.INPUT_FIELD; 3263 } else if ($element.hasClass("facet")) { 3264 // Find the closest parent container for the facet group. 3265 var $facetGroup = $element.closest(".facet-filters"); 3266
3267 // Within that group, find the heading element. 3268 // This looks for either an <h4> or an element with the .facet-filters__heading class. 3269 var $heading = $facetGroup 3270 .find("h4, .facet-filters__heading") 3271 .first(); 3272 3273 // Get the group's title from the heading's data attribute. 3274 var facetGroupText = $heading.data("asc-element-title"); 3275 3276 // Get the selected filter's text. 3277 // It prioritizes the data attribute and falls back to the element's inner text. 3278 var facetText = 3279 $element.data("asc-element-title") || 3280 modules.utilities.getInnerText($element); 3281 3282 var facetCount = window 3283 .getComputedStyle($element[0], ":after") 3284 .getPropertyValue("content") 3285 .replace(/"/g, "") 3286 .replace(/:/g, "") 3287 .trim(); 3288 3289 // if the pseudo element contains a facet count, 3290 // include it in the asc_element_text value. 3291 if (facetCount) { 3292 facetCount = facetCount 3293 .replace(/"/g, "") 3294 .replace(/:/g, "") 3295 .trim(); 3296 facetText = `${facetText} (${facetCount})`; 3297 } 3298 3299 // handle normal facet clicks 3300 ascEventData.event = EVENT.ASC_ELEMENT_CONFIGURATION; 3301 ascEventData.event_action_result = EVENT_ACTION_RESULT.SEARCH; 3302 ascEventData.element_state = $element.hasClass("active") 3303 ? ELEMENT_STATE.ACTIVE 3304 : ELEMENT_STATE.INACTIVE; 3305 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3306 ascEventData.element_subtype = ELEMENT_SUBTYPE.TOGGLE; 3307 ascEventData.element_title = facetGroupText; 3308 ascEventData.element_text = facetText; 3309 ascEventData.element_value = 3310 modules.utilities.getInnerText($element); 3311 } else if ($element.hasClass("facet-filters__clear")) { 3312 // handle clear search filters button 3313 ascEventData.element_text = $element[0].innerText.trim(); 3314 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3315 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3316 ascEventData.event_action_result = EVENT_ACTION_RESULT.SEARCH; 3317 ascEventData.link_url = null; 3318 } else if ($element.hasClass("facet-filters__heading")) { 3319 // handle facet filter expand/collapse clicks 3320 ascEventData.event = EVENT.ASC_ELEMENT_CONFIGURATION; 3321 ascEventData.event_action_result = $element 3322 .parent() 3323 .hasClass("collapsed") 3324 ? EVENT_ACTION_RESULT.OPEN 3325 : EVENT_ACTION_RESULT.CLOSE; 3326 ascEventData.event_action = EVENT_ACTION.CLICK; 3327 ascEventData.element_state = ELEMENT_STATE.ACTIVE; 3328 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3329 ascEventData.element_subtype = ELEMENT_SUBTYPE.DROPDOWN; 3330 //ascEventData.element_title = modules.utilities.getInnerText($element); 3331 ascEventData.element_text = 3332 modules.utilities.getInnerText($element); 3333 ascEventData.element_value = 3334 modules.utilities.getInnerText($element); 3335 } else if ($element.hasClass("pagination__list-item")) { 3336 // handle inventory grid pagination previous/next clicks 3337 var description; 3338 const eventProperty = $element.data("event"); 3339 3340 if (eventProperty === "Go To Page") { 3341 description = $element.data("value"); 3342 } else { 3343 description = 3344 eventProperty.indexOf("Next") > -1 ? "Next" : "Prev"; 3345 } 3346 3347 ascEventData.element_text = description; 3348 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3349 ascEventData.element_subtype = ELEMENT_SUBTYPE.NAV_BUTTON; 3350 ascEventData.event_action_result = EVENT_ACTION_RESULT.SEARCH; 3351 } else if ( 3352 $element.hasClass(
3353 "wts-vehicle-carousel__navigation-action--left" 3354 ) 3355 ) { 3356 // handle similar/recent carousel previous clicks 3357 const descriptionPrefix = modules.utilities 3358 .getCarouselDescription($element) 3359 .replaceAll("-", "_"); 3360 3361 ascEventData.element_text = `${descriptionPrefix}_previous`; 3362 ascEventData.element_title = `${descriptionPrefix}_previous`; 3363 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3364 ascEventData.element_subtype = ELEMENT_SUBTYPE.NAV_BUTTON; 3365 ascEventData.event_action_result = EVENT_ACTION_RESULT.SEARCH; 3366 } else if ( 3367 $element.hasClass( 3368 "wts-vehicle-carousel__navigation-action--right" 3369 ) 3370 ) { 3371 // handle similar/recent carousel next clicks 3372 const descriptionPrefix = modules.utilities 3373 .getCarouselDescription($element) 3374 .replaceAll("-", "_"); 3375 3376 ascEventData.element_text = `${descriptionPrefix}_next`; 3377 ascEventData.element_title = `${descriptionPrefix}_next`; 3378 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3379 ascEventData.element_subtype = ELEMENT_SUBTYPE.NAV_BUTTON; 3380 ascEventData.event_action_result = EVENT_ACTION_RESULT.SEARCH; 3381 } else if ( 3382 $element.hasClass("wts-vehicle-carouse__lite-item-link") 3383 ) { 3384 // handle clicking a vehicle tile within the similar/recent carousels 3385 ascEventData.element_text = $element 3386 .find(".wts-vehicle-card__image-container") 3387 .css("url"); 3388 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3389 ascEventData.element_subtype = ELEMENT_SUBTYPE.IMAGE; 3390 ascEventData.link_url = $element.attr("href"); 3391 } else if ($element.hasClass("truck-actions__interested")) { 3392 // handle I'm Interested clicks from the VSP 3393 ascEventData.element_type = ELEMENT_TYPE.FORM; 3394 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3395 ascEventData.element_text = 3396 modules.utilities.getInnerText($element); 3397 ascEventData.items = getMatchingItem($element); 3398 } else if ($element.hasClass("im-interested__action")) { 3399 // handle I'm Interested clicks from the VDP 3400 ascEventData.element_text = 3401 modules.utilities.getInnerText($element); 3402 ascEventData.element_type = ELEMENT_TYPE.POPUP; 3403 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3404 ascEventData.items = getMatchingItem($element); 3405 } else if ( 3406 $element.hasClass("featured-photo-gallery__see-all-photos") 3407 ) { 3408 // handle See All Photos click from the VDP 3409 ascEventData.event = EVENT.ASC_MEDIA_INTERACTION; 3410 ascEventData.media_type = MEDIA_TYPE.GALLERY; 3411 ascEventData.items = getMatchingItem($element); 3412 ascEventData.affiliation = window.asc_datalayer.affiliation; 3413 } else if ($element.hasClass("featured-vehicles-button")) { 3414 // handle Featured Vehicles button click 3415 ascEventData.element_text = $element[0].textContent; 3416 ascEventData.element_type = ELEMENT_TYPE.HEADER; 3417 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3418 ascEventData.event_action_result = EVENT_ACTION_RESULT.SHOWN; 3419 } else if ( 3420 $element.is(".action-link, .email-button, .call-button") 3421 ) { 3422 // handle email or call action link above vocational carousel 3423 ascEventData.element_text = $element[0].textContent; 3424 ascEventData.element_type = ELEMENT_TYPE.HEADER;
3425 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3426 ascEventData.event_action_result = EVENT_ACTION_RESULT.POPUP; 3427 } else if ($element.is(".support-link")) { 3428 // handle support modal in page footer 3429 ascEventData.element_type = ELEMENT_TYPE.FOOTER; 3430 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3431 ascEventData.event_action_result = EVENT_ACTION_RESULT.POPUP; 3432 } else if ( 3433 $element.hasClass("crestline-transportation__get-quote") 3434 ) { 3435 // handle Crestline button click 3436 (ascEventData.element_text = 3437 modules.utilities.getInnerText($element)), 3438 (ascEventData.element_type = 3439 ELEMENT_TYPE.DIGITAL_RETAILING_TOOL); 3440 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3441 ascEventData.event_action_result = EVENT_ACTION_RESULT.POPUP; 3442 } else if ($element.hasClass("crestline__generate-quote")) { 3443 // handle Crestline button click 3444 (ascEventData.element_text = 3445 modules.utilities.getInnerText($element)), 3446 (ascEventData.element_type = 3447 ELEMENT_TYPE.DIGITAL_RETAILING_TOOL); 3448 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3449 ascEventData.event_action_result = EVENT_ACTION_RESULT.POPUP; 3450 } else if ($element.hasClass("tf-gridFinancingLink__button")) { 3451 // handle taycor financing button click 3452 (ascEventData.element_text = 3453 modules.utilities.getInnerText($element)), 3454 (ascEventData.element_type = 3455 ELEMENT_TYPE.DIGITAL_RETAILING_TOOL); 3456 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3457 } else if ($element.parent(".body-type").parent(".item").length) { 3458 // handle Vocational Carousel wireframe click 3459 const parentDiv = $element.parent(".body-type").parent(".item"); 3460 3461 ascEventData.event = EVENT.ASC_ELEMENT_CONFIGURATION; 3462 ascEventData.event_action_result = EVENT_ACTION_RESULT.SEARCH; 3463 ascEventData.element_state = parentDiv.hasClass("selected") 3464 ? ELEMENT_STATE.ACTIVE 3465 : ELEMENT_STATE.INACTIVE; 3466 ascEventData.element_type = ELEMENT_TYPE.SEARCH_TOOL; 3467 ascEventData.element_subtype = ELEMENT_SUBTYPE.IMAGE; 3468 //ascEventData.element_title = parentDiv.children("h3")[0].innerText; 3469 ascEventData.element_text = 3470 parentDiv.children("h3")[0].innerText; 3471 ascEventData.element_value = 3472 parentDiv.children("h3")[0].innerText; 3473 } else if ($element.hasClass("navigation-open-button")) { 3474 ascEventData.event = EVENT.ASC_MENU_INTERACTION; 3475 ascEventData.element_type = ELEMENT_TYPE.MENU; 3476 ascEventData.element_subtype = ELEMENT_SUBTYPE.HAMBURGER_MENU; 3477 ascEventData.element_text = "Open Mobile Menu"; 3478 ascEventData.event_action_result = EVENT_ACTION_RESULT.OPEN; 3479 } else if ($element.hasClass("navigation-close-button")) { 3480 ascEventData.event = EVENT.ASC_MENU_INTERACTION; 3481 ascEventData.element_type = ELEMENT_TYPE.MENU; 3482 ascEventData.element_subtype = ELEMENT_SUBTYPE.HAMBURGER_MENU; 3483 ascEventData.element_text = "Close Mobile Menu"; 3484 ascEventData.event_action_result = EVENT_ACTION_RESULT.CLOSE; 3485 } else if ($element.is(".ez-order-button--trigger")) { 3486 // handle EZOrder modal button clicks 3487 ascEventData.element_text = $element[0].textContent; 3488 ascEventData.element_type = ELEMENT_TYPE.POPUP; 3489 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3490 ascEventData.event_action_result = EVENT_ACTION_RESULT.POPUP; 3491 } else if ($element.offsetParent("wts-webbuy-buttons")) { 3492 (ascEventData.element_text = 3493 modules.utilities.getInnerText($element)), 3494 (ascEventData.element_type = 3495 ELEMENT_TYPE.DIGITAL_RETAILING_TOOL); 3496 ascEventData.element_subtype = ELEMENT_SUBTYPE.CTA_BUTTON; 3497 ascEventData.event_action_result = EVENT_ACTION_RESULT.POPUP; 3498 } else { 3499 // presently an unhandled event so return which means don't fire an event 3500 return; 3501 } 3502 3503 // Route to specific event if currently set to asc_cta_interaction and properties indicate a better fit 3504 if (ascEventData.event === EVENT.ASC_CTA_INTERACTION) { 3505 if (ascEventData.element_type === ELEMENT_TYPE.MENU) { 3506 ascEventData.event = EVENT.ASC_MENU_INTERACTION; 3507 } else if ( 3508 ascEventData.element_type === ELEMENT_TYPE.GALLERY || 3509 ascEventData.element_type === ELEMENT_TYPE.VIDEO || 3510 ascEventData.media_type 3511 ) { 3512 ascEventData.event = EVENT.ASC_MEDIA_INTERACTION; 3513 } else if (ascEventData.element_type === ELEMENT_TYPE.FORM) { 3514 ascEventData.event = EVENT.ASC_FORM_ENGAGEMENT; 3515 } else if ( 3516 ascEventData.element_type === ELEMENT_TYPE.CHAT_TOOL || 3517 ascEventData.comm_type 3518 ) { 3519 ascEventData.event = EVENT.ASC_COMM_ENGAGEMENT; 3520 } else if ( 3521 ascEventData.element_state || 3522 ascEventData.element_type === ELEMENT_TYPE.PAGINATION || 3523 ascEventData.element_type === ELEMENT_TYPE.SEARCH_TOOL 3524 ) { 3525 ascEventData.event = EVENT.ASC_ELEMENT_CONFIGURATION; 3526 } 3527 } 3528 3529 if ( 3530 [ 3531 EVENT.ASC_CTA_INTERACTION, 3532 EVENT.ASC_CLICK_TO_CALL, 3533 EVENT.ASC_MENU_INTERACTION, 3534 ].includes(ascEventData.event) 3535 ) { 3536 ascEventData = { 3537 ...ascEventData, 3538 ...formatData(ascEventData.items), 3539 }; 3540 delete ascEventData.items; 3541 } 3542 3543 trackAscEvent(ascEventData); 3544 }, 3545 3546 /** 3547 * Tracks vocational carousel page change events. 3548 * @param {any} event Event data giving context to the vocational carousel's present state. 3549 */ 3550 trackVocationalCarouselPageChange: function (event) { 3551 if (!_enableAscTracking) { 3552 return; 3553 } 3554 3555 // ignore carousel events triggered by window resize 3556 if (_isResizing) { 3557 return; 3558 } 3559 3560 const pageChangeEventData = event.detail; 3561 const pageNumber = pageChangeEventData.page.index; 3562 const pageSize = pageChangeEventData.page.size; 3563 const pageCount = pageChangeEventData.page.count; 3564 3565 // determine if the user clicked next or previous arrow 3566 var direction; 3567 if (_vocationalCarouselCurrentPage === 0) { 3568 if (pageNumber === pageSize) { 3569 direction = "previous"; 3570 _vocationalCarouselCurrentPage = pageSize; 3571 } else { 3572 direction = "next"; 3573 _vocationalCarouselCurrentPage = pageNumber; 3574 } 3575 } else { 3576 direction = 3577 _vocationalCarouselCurrentPage <= pageNumber 3578 ? "next" 3579 : _vocationalCarouselCurrentPage === pageCount - 1 && 3580 pageNumber === 0 3581 ? "next" 3582 : "previous"; 3583 _vocationalCarouselCurrentPage = pageNumber; 3584 } 3585 3586 var ascEventData = { 3587 event: event.type, 3588 page_type: window.asc_datalayer.page_type, 3589 event_owner: window.asc_datalayer.affiliation, 3590 element_type: ELEMENT_TYPE.SEARCH_TOOL, 3591 element_text: direction, 3592 element_title: direction, 3593 event_action: EVENT_ACTION.CLICK, 3594 event_action_result: EVENT_ACTION_RESULT.SEARCH, 3595 ...formatData(window.asc_datalayer.items), 3596 }; 3597 3598 trackAscEvent(ascEventData); 3599 }, 3600 3601 /** 3602 * Tracks pagination control change events. 3603 * @param {any} event Event data giving context ot the pagination control that changed. 3604 */ 3605 trackPaginationControlChange: function (event) { 3606 if (!_enableAscTracking) { 3607 return; 3608 } 3609 3610 var $element = $(event.detail); 3611 3612 var ascEventData = { 3613 event: event.type, 3614 page_type: window.asc_datalayer.page_type, 3615 event_owner: window.asc_datalayer.affiliation, 3616 event_action_result: EVENT_ACTION_RESULT.SEARCH, 3617 ...formatData(window.asc_datalayer.items), 3618 }; 3619 3620 // determine if the control is located in the top or bottom nav as well as which control is being interacted with 3621 const $parentElement = $element.parents(".pagination-top"); 3622 const location = $parentElement.length ? "header" : "footer"; 3623 3624 ascEventData.element_type = location; 3625 ascEventData.event_action = EVENT_ACTION.CLICK; 3626 ascEventData.element_text = 3627 $element[0].selectedOptions[0].innerText; 3628 ascEventData.element_value = $element[0].selectedOptions[0].value; 3629 ascEventData.element_title = $element.is("select") 3630 ? $($element[0].selectedOptions[0]).data("asc-element-title") 3631 : $element.data("asc-element-title"); 3632 ascEventData.element_position = `${ 3633 location === "header" ? "top" : "bottom" 3634 }_${ 3635 $element.hasClass("items-per-page__input") ? "center" : "right" 3636 }`; 3637 ascEventData.element_state = $element.disabled 3638 ? "inactive" 3639 : "active"; 3640 ascEventData.element_subtype = ELEMENT_SUBTYPE.DROPDOWN; 3641 3642 trackAscEvent(ascEventData); 3643 }, 3644 3645 /** 3646 * Sets up the _formInteractions static member with the ID of the form being edited. 3647 * @param {any} event Event data giving context to the element within the form that was focused. 3648 */ 3649 initializeFormStart: function (event) { 3650 if (!_enableAscTracking) { 3651 return; 3652 } 3653 3654 const $element = $(event.detail.currentTarget); 3655 const $customForm = $element.closest(".wts-custom-forms"); 3656 3657 if ($customForm.length) { 3658 const formKey = 3659 $customForm.attr("id") || 3660 "custom-form-" + $(".wts-custom-forms").index($customForm); 3661 3662 if (_customFormInteractions.has(formKey)) { 3663 return; 3664 } 3665 3666 _customFormInteractions.set(formKey, new Map()); 3667 3668 if (modules.AscDelegatedTracking) { 3669 const ctx = 3670 modules.AscDelegatedTracking.getFormContext($customForm); 3671 const fieldTitle = 3672 modules.AscDelegatedTracking.getFormFieldElementTitle( 3673 $element 3674 ); 3675 const fieldText = 3676 modules.AscDelegatedTracking.getFormFieldElementText( 3677 $element 3678 ); 3679 const fieldValue = 3680 modules.AscDelegatedTracking.getFormFieldElementValue( 3681 $element 3682 ); 3683 3684 trackAscEvent({ 3685 event: EVENT.ASC_FORM_ENGAGEMENT, 3686 event_owner: window.asc_datalayer.affiliation, 3687 page_type: ctx.page_type, 3688 comm_type: COMM_TYPE.FORM, 3689 comm_status: COMM_STATUS.START, 3690 element_text: fieldText, 3691 element_title: fieldTitle, 3692 element_value: fieldValue, 3693 department: ctx.department || DEPARTMENT.SALES, 3694 form_name: ctx.form_name, 3695 form_type: ctx.form_type, 3696 }); 3697 } 3698 3699 return; 3700 } 3701 3702 const parentFormID = $element 3703 .parents(".im-interested-form") 3704 .attr("id"); 3705 3706 if (parentFormID) { 3707 // I'm Interested form 3708 const formInteraction = _formInteractions.get(parentFormID); 3709 3710 if (formInteraction) { 3711 // don't fire duplicate form start events 3712 return; 3713 } 3714 3715 _formInteractions.set(parentFormID, new Map()); 3716 } else { 3717 // other contact forms (email, phone) 3718 } 3719 }, 3720 3721 /** 3722 * Tracks user engagement with a form - when a form's input changes. 3723 * @param {any} event Event data giving context to the element within the form that was changed. 3724 */ 3725 trackFormEngage: function (event) { 3726 if (!_enableAscTracking) { 3727 return; 3728 } 3729 3730 const $element = $(event.detail.currentTarget); 3731 const $customForm = $element.closest(".wts-custom-forms"); 3732 const hiddenInventoryID = $(".hidden-inventory-id").val(); 3733 const elementIdentifier = 3734 $element.attr("name") || $element.attr("id"); 3735 let elementText = ""; 3736 let elementTitle = $element.data("asc-element-title") || ""; 3737 let elementValue = ""; 3738 let foundItems = [ 3739 window.asc_datalayer.items.find( 3740 (i) => i.item_inventory_id === hiddenInventoryID 3741 ), 3742 ]; 3743 3744 var customFormPageType = window.asc_datalayer.page_type; 3745 var customFormName = ""; 3746 var customFormType = FORM_TYPE.CONSUMER_CONTACT; 3747 var customFormDepartment = DEPARTMENT.SALES; 3748 3749 if (modules.AscDelegatedTracking) { 3750 elementTitle = 3751 modules.AscDelegatedTracking.getFormFieldElementTitle( 3752 $element 3753 ) || 3754 elementTitle || 3755 ""; 3756 elementText = 3757 modules.AscDelegatedTracking.getFormFieldElementText( 3758 $element 3759 ); 3760 elementValue = 3761 modules.AscDelegatedTracking.getFormFieldElementValue( 3762 $element 3763 ); 3764 } 3765 3766 if ($customForm.length && modules.AscDelegatedTracking) { 3767 const customCtx = 3768 modules.AscDelegatedTracking.getFormContext($customForm); 3769 customFormPageType = customCtx.page_type; 3770 customFormName = customCtx.form_name; 3771 customFormType = customCtx.form_type; 3772 customFormDepartment = customCtx.department || DEPARTMENT.SALES; 3773 } 3774 3775 const ascEventData = { 3776 // Required 3777 event: event.type, 3778 event_owner: window.asc_datalayer.affiliation, 3779 page_type: customFormPageType, 3780 comm_type: COMM_TYPE.FORM, 3781 comm_status: event.d
3781etail.commStatus || COMM_STATUS.ENGAGE, 3782 element_text: elementText || "", 3783 element_title: elementTitle, 3784 element_value: elementValue, 3785 department: customFormDepartment, 3786 form_name: 3787 customFormName || 3788 event.detail.formName || 3789 $element 3790 .parents("section") 3791 .siblings(".modal-heading")[0] 3792 ?.innerText.trim() || 3793 ($element.attr("id") && 3794 $element.attr("id").startsWith("ez-order") 3795 ? "EZ Order" 3796 : "") || 3797 ($element.attr("id") && 3798 $element.attr("id").startsWith("NoResultsForm") 3799 ? "No Results" 3800 : ""), 3801 form_type: $customForm.length 3802 ? customFormType 3803 : FORM_TYPE.CONSUMER_CONTACT, 3804 // Optional 3805 ...formatData(foundItems), 3806 }; 3807 3808 // Customize for callback form start 3809 if ( 3810 event.detail.formName === "Callback Form" || 3811 event.detail.eventName === "Request a Callback" 3812 ) { 3813 ascEventData.element_text = "Request a Callback"; 3814 ascEventData.element_title = 3815 $element.data("asc-element-title") || ""; 3816 ascEventData.form_name = "Callback Form"; 3817 ascEventData.form_type = FORM_TYPE.CONSUMER_CONTACT; 3818 ascEventData.department = DEPARTMENT.SALES; 3819 ascEventData.comm_status = COMM_STATUS.START; // Ensure start for callback reveal 3820 } 3821 3822 const parentFormID = $element 3823 .parents(".im-interested-form") 3824 .attr("id"); 3825 3826 if (parentFormID) { 3827 const formInteraction = _formInteractions.get(parentFormID); 3828 3829 if (!formInteraction) { 3830 return; 3831 } 3832 3833 var elementInteraction = formInteraction.get(elementIdentifier); 3834 3835 if (elementInteraction) { 3836 // don't duplicate events for the same control 3837 return; 3838 } 3839 3840 formInteraction.set(elementIdentifier, 1); 3841 } 3842 3843 if ($customForm.length) { 3844 const formKey = 3845 $customForm.attr("id") || 3846 "custom-form-" + 3847 $(".wts-custom-forms").index($customForm); 3848 const formInteraction = _customFormInteractions.get(formKey); 3849 3850 if (!formInteraction) { 3851 return; 3852 } 3853 3854 if (formInteraction.get(elementIdentifier)) { 3855 return; 3856 } 3857 3858 formInteraction.set(elementIdentifier, 1); 3859 } 3860 3861 trackAscEvent(ascEventData); 3862 }, 3863 3864 /** 3865 * Tracks a user submitting a form. 3866 * @param {any} event Event data giving context to the form that was submitted. 3867 */ 3868 trackFormSubmission: function (event) { 3869 if (!_enableAscTracking) { 3870 return; 3871 } 3872 3873 let foundItems = [ 3874 window.asc_datalayer.items.find( 3875 (i) => i.item_inventory_id === event.detail.inventoryID 3876 ), 3877 ]; 3878 3879 var submissionPageType = window.asc_datalayer.page_type; 3880 var submissionFormName = event.detail.messageType; 3881 var submissionFormType = FORM_TYPE.CONSUMER_CONTACT; 3882 var submissionDepartment = DEPARTMENT.SALES; 3883 3884 if (event.detail.formElement && modules.AscDelegatedTracking) { 3885 const ctx = modules.AscDelegatedTracking.getFormContext( 3886 event.detail.formElement 3887 ); 3888 submissionPageType = ctx.page_type; 3889 submissionFormName = ctx.form_name || submissionFormName; 3890 submissionFormType = ctx.form_type; 3891 submissionDepartment = ctx.department || DEPARTMENT.SALES; 3892 } 3893 3894 const ascEventData = { 3895 event: event.type, 3896 event_owner: window.asc_datalayer.affiliation, 3897 page_type: submissionPageType, 3898 comm_type: COMM_TYPE.FORM, 3899 comm_outcome: COMM_OUTCOME.OPPORTUNITY, 3900 submission_id: event.detail.uniqueID, 3901 element_text: submissionFormName, 3902 department: submissionDepartment, 3903 form_name: submissionFormName, 3904 form_type: submissionFormType, 3905 ...formatData(foundItems), 3906 }; 3907 3908 trackAscEvent(ascEventData); 3909 3910 if (modules.AscDelegatedTracking) { 3911 var secondaryEvent = 3912 modules.AscDelegatedTracking.resolveSecondarySubmissionEvent( 3913 submissionDepartment, 3914 submissionFormType
3915 ); 3916 if (secondaryEvent !== EVENT.ASC_FORM_SUBMISSION) { 3917 trackAscEvent({ 3918 ...ascEventData, 3919 event: secondaryEvent, 3920 }); 3921 } 3922 } 3923 }, 3924 3925 trackFormClose: function (event) { 3926 if (!_enableAscTracking) { 3927 return; 3928 } 3929 3930 let foundItems = [ 3931 window.asc_datalayer.items.find( 3932 (i) => i.item_inventory_id === event.detail.inventoryID 3933 ), 3934 ]; 3935 3936 const ascEventData = { 3937 // Required 3938 event: event.type, 3939 event_owner: window.asc_datalayer.affiliation, 3940 page_type: window.asc_datalayer.page_type, 3941 comm_type: COMM_TYPE.FORM, 3942 comm_status: COMM_STATUS.CLOSE, 3943 comm_outcome: COMM_OUTCOME.NOT_OPPORTUNITY, 3944 submission_id: event?.detail?.uniqueID, 3945 element_text: event.detail.eventName, 3946 department: DEPARTMENT.SALES, 3947 form_name: event.detail.formName, 3948 form_type: FORM_TYPE.CONSUMER_CONTACT, 3949 // Optional 3950 ...formatData(foundItems), 3951 }; 3952 trackAscEvent(ascEventData); 3953 }, 3954 3955 /** 3956 * Tracks a media interaction click. 3957 * @param {any} event Event data giving context to the interaction click. 3958 */ 3959 trackMediaInteractionClick: function (event) { 3960 if (!_enableAscTracking) { 3961 return; 3962 } 3963 3964 let foundItems = [ 3965 window.asc_datalayer.items.find( 3966 (i) => i.item_inventory_id === event.detail.inventoryID 3967 ), 3968 ]; 3969 3970 const ascEventData = { 3971 // Required 3972 event: event.type, 3973 event_owner: window.asc_datalayer.affiliation, 3974 page_type: window.asc_datalayer.page_type, 3975 event_action: event.detail.eventAction, 3976 event_action_result: event.detail.eventActionResult, 3977 media_type: event.detail.mediaType, 3978 link_url: event.detail.linkUrl, 3979 // Optional 3980 affilition: window.asc_datalayer.affiliation, 3981 ...formatData(foundItems), 3982 }; 3983 3984 //check if ad 3985 $element = $(event.detail.currentTarget); 3986 if ( 3987 $element.hasClass("dealership-top-image") || 3988 $element.hasClass("dealership-ads-left-panel-image") 3989 ) { 3990 ascEventData.media_type = MEDIA_TYPE.IMAGE; 3991 ascEventData.link_url = $element 3992 .children() 3993 .filter("a")[0]?.href; 3994 ascEventData.event_action = EVENT_ACTION.CLICK; 3995 ascEventData.event_action_result = EVENT_ACTION_RESULT.OPEN; 3996 } 3997 3998 trackAscEvent(ascEventData); 3999 }, 4000 4001 /** 4002 * Tracks a generic interaction click event. 4003 * @param {any} event Event data giving context to the interaction click. 4004 */ 4005 trackInteractionClick: function (event) { 4006 if (!_enableAscTracking) { 4007 return; 4008 } 4009 4010 let foundItems = event.detail.inventoryID 4011 ? [ 4012 window.asc_datalayer.items.find( 4013 (i) => 4014 i.item_inventory_id === event.detail.inventoryID 4015 ), 4016 ] 4017 : window.asc_datalayer.items; 4018 4019 var ascEventData = { 4020 // Required 4021 event: event.type, 4022 event_owner: window.asc_datalayer.affiliation, 4023 page_type: window.asc_datalayer.page_type, 4024 element_text: event.detail.elementText, 4025 element_title: event.detail.elementTitle, 4026 element_type: event.detail.elementType, 4027 element_subtype: event.detail.elementSubtype, 4028 event_action: event.detail.eventAction, 4029 event_action_result: event.detail.eventActionResult, 4030 ...formatData(foundItems), 4031 }; 4032 4033 // Route to specific event if applicable 4034 let eventName = ascEventData.event; 4035 if ( 4036 ascEventData.element_type === 4037 AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.MENU 4038 ) { 4039 eventName = AscTracking.ASC_MAPPINGS.EVENT.ASC_MENU_INTERACTION; 4040 } else if ( 4041 ascEventData.element_type === 4042 AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY || 4043 ascEventData.element_type === 4044 AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.VIDEO || 4045 ascEventData.media_type 4046 ) { 4047 eventName = 4048 AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION; 4049 } else if ( 4050 ascEventData.element_type === 4051 AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.FORM 4052 ) { 4053 eventName = AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_ENGAGEMENT; 4054 } else if ( 4055 ascEventData.element_type === 4056 AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.CHAT_TOOL || 4057 ascEventData.comm_type 4058 ) { 4059 eventName = AscTracking.ASC_MAPPINGS.EVENT.ASC_COMM_ENGAGEMENT; 4060 } else if ( 4061 ascEventData.element_state || 4062 ascEventData.element_type === 4063 AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.PAGINATION || 4064 ascEventData.element_type === 4065 AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.SEARCH_TOOL 4066 ) { 4067 eventName = 4068 AscTracking.ASC_MAPPINGS.EVENT.ASC_ELEMENT_CONFIGURATION; 4069 } 4070 4071 ascEventData.event = eventName; 4072 4073 trackAscEvent(ascEventData); 4074 }, 4075 }; 4076 4077 // "private static" functions: 4078 4079 /** 4080 * Helper method used to actually push event data into window.dataLayer via the gtag function (see _GoogleAnalytics.cshtml). 4081 * @param {any} ascEventData Event data giving context to something that needs to be tracked in GA. 4082 */ 4083 trackAscEvent = function (ascEventData) { 4084 if (!_enableAscTracking) { 4085 return; 4086 } 4087 4088 if (_enableDebug) { 4089 validateParams(ascEventData); 4090 } 4091 4092 window._logGA("", ascEventData.event, "", ascEventData);
4093 ascEventData.send_to = _ascMeasurementIDs; 4094 ascEventData = { 4095 ...ascEventData, 4096 ...{ experiment_variant: window.getExperimentVariant() }, 4097 }; 4098 gtag("event", ascEventData.event, ascEventData); 4099 4100 window.asc_datalayer.events.unshift(ascEventData); 4101 }; 4102 4103 validateParams = function (ascEventData) { 4104 if (!_enableAscTracking && !ascEventData) { 4105 return; 4106 } 4107 4108 const requiredParams = REQUIRED_PARAMS[ascEventData.event]; 4109 //const optionalParams = OPTIONAL_PARAMS[ascEventData.event]; 4110 4111 if (requiredParams?.length) { 4112 const missing = requiredParams.filter( 4113 (key) => 4114 ascEventData[key] === undefined || 4115 ascEventData[key] === null 4116 ); 4117 4118 console.clear(); 4119 4120 if (missing.length > 0) { 4121 console.error( 4122 `${ascEventData.event} is missing required fields: ${missing}` 4123 ); 4124 } 4125 } 4126 4127 const formatted = Object.fromEntries( 4128 Object.entries(ascEventData).map(([key, value]) => [ 4129 key, 4130 (value === null || 4131 value === "" || 4132 typeof value === "undefined") && 4133 requiredParams.includes(key) 4134 ? "â MISSING REQUIRED" 4135 : Array.isArray(value) 4136 ? JSON.stringify(value) 4137 : value, 4138 ]) 4139 ); 4140 4141 console.table(formatted); 4142 }; 4143 4144 formatData = function (data) { 4145 if (!data) return; 4146 4147 const rules = { 4148 item_category: { dedupe: true, sort: true }, 4149 item_color: { dedupe: true, sort: true }, 4150 item_condition: { dedupe: true, sort: true }, 4151 item_fuel_type: { dedupe: true, sort: true }, 4152 item_id: { dedupe: false, sort: false }, 4153 item_inventory_date: { 4154 dedupe: true, 4155 sort: true, 4156 isDate: true, 4157 range: true, 4158 }, 4159 item_make: { dedupe: true, sort: true }, 4160 item_model: { dedupe: true, sort: true }, 4161 item_number: { dedupe: false, sort: false }, 4162 item_payment: { dedupe: true, sort: true }, 4163 item_type: { dedupe: true, sort: true }, 4164 item_variant: { dedupe: true, sort: true }, 4165 item_year: { dedupe: true, sort: true, range: true }, 4166 item_price: { dedupe: true, sort: true, range: true }, 4167 item_inventory_id: { dedupe: false, sort: false }, 4168 item_location: { 4169 dedupe: true, 4170 sort: true, 4171 dedupeCaseInsensitive: true, 4172 }, 4173 }; 4174 4175 // Step 1: Collect values 4176 const intermediate = {}; 4177 4178 for (const obj of data) { 4179 for (const key in obj) { 4180 const value = obj[key]; 4181 4182 // Skip nulls and empty strings 4183 if (value === null || value === "") continue; 4184 4185 const { dedupe = true, dedupeCaseInsensitive = false } = 4186 rules[key] || {}; 4187 4188 if (!intermediate[key]) { 4189 intermediate[key] = dedupe 4190 ? dedupeCaseInsensitive 4191 ? { seen: new Set(), values: [] } 4192 : new Set() 4193 : []; 4194 } 4195 4196 if (!dedupe) { 4197 intermediate[key].push(value); 4198 } else if (dedupeCaseInsensitive) { 4199 const lc = 4200 typeof value === "string" ? value.toLowerCase() : value; 4201 if (!intermediate[key].seen.has(lc)) { 4202 intermediate[key].seen.add(lc); 4203 intermediate[key].values.push(value); 4204 } 4205 } else { 4206 intermediate[key].add(value); 4207 } 4208 } 4209 } 4210 4211 // Step 2: Format output
4212 const condensed = {}; 4213 4214 for (const key in intermediate) { 4215 const { 4216 sort = true, 4217 isDate = false, 4218 dedupe = true, 4219 dedupeCaseInsensitive = false, 4220 range = false, 4221 } = rules[key] || {}; 4222 let values; 4223 4224 if (!dedupe) { 4225 values = intermediate[key]; 4226 } else if (dedupeCaseInsensitive) { 4227 values = intermediate[key].values; 4228 } else { 4229 values = Array.from(intermediate[key]); 4230 } 4231 4232 if (sort) { 4233 values.sort((a, b) => { 4234 const valA = normalizeDate(a); 4235 const valB = normalizeDate(b); 4236 4237 if (isDate && isDateLike(valA) && isDateLike(valB)) { 4238 return new Date(valA) - new Date(valB); 4239 } 4240 4241 if (typeof valA === "number" && typeof valB === "number") { 4242 return valA - valB; 4243 } 4244 4245 return String(valA).localeCompare(String(valB)); 4246 }); 4247 } 4248 4249 if (range && values.length > 1) { 4250 if (isDate) { 4251 condensed[key] = `${formatDate(values[0])}-${formatDate( 4252 values[values.length - 1] 4253 )}`; 4254 } else { 4255 condensed[key] = `${values[0]}-${ 4256 values[values.length - 1] 4257 }`; 4258 } 4259 } else { 4260 condensed[key] = values.join("|"); 4261 } 4262 } 4263 4264 return condensed; 4265 }; 4266 4267 // Helpers 4268 normalizeDate = function (value) { 4269 return typeof value === "string" ? value.replace(/\//g, "-") : value; 4270 }; 4271 4272 isDateLike = function (value) { 4273 return ( 4274 typeof value === "string" && 4275 !isNaN(Date.parse(normalizeDate(value))) 4276 ); 4277 }; 4278 4279 formatDate = function (value) { 4280 const date = new Date(normalizeDate(value)); 4281 const yyyy = date.getFullYear(); 4282 const mm = String(date.getMonth() + 1).padStart(2, "0"); // months are 0-based 4283 const dd = String(date.getDate()).padStart(2, "0"); 4284 return `${yyyy}/${mm}/${dd}`; 4285 }; 4286 4287 // exports 4288 modules.AscTracking = AscTracking; 4289 modules.AscTracking.ASC_MAPPINGS = ASC_MAPPINGS; 4290})(window.wts, window.wts.modules); 4291; 4292(function (wts, modules) { 4293 var AscTracking = modules.AscTracking; 4294 var ASC = AscTracking.ASC_MAPPINGS; 4295 4296 var ATTR_TO_PARAM = { 4297 "page-type": "page_type", 4298 type: "element_type", 4299 title: "element_title", 4300 subtype: "element_subtype", 4301 color: "element_color", 4302 "form-name": "form_name",
4303 "form-type": "form_type", 4304 department: "department", 4305 "promotion-name": "promotion_name", 4306 "element-title": "element_title", 4307 }; 4308 4309 var PII_FIELD_NAMES = { 4310 FirstName: true, 4311 LastName: true, 4312 Lastname: true, 4313 Email: true, 4314 EmailAddress: true, 4315 Phone: true, 4316 MobilePhone: true, 4317 OfficePhone: true, 4318 }; 4319 4320 function findAscTarget(el) { 4321 if (!el) { 4322 return null; 4323 } 4324 var node = el.nodeType === 1 ? el : el.parentElement; 4325 while (node && node !== document.body) { 4326 if (node.hasAttribute && node.hasAttribute("data-track-asc")) { 4327 return node; 4328 } 4329 node = node.parentElement; 4330 } 4331 return null; 4332 } 4333 4334 function normalizePageType(pageType) { 4335 if (pageType === "custom_page") { 4336 return "custom"; 4337 } 4338 return pageType; 4339 } 4340 4341 function resolvePageType(el) { 4342 var node = el && (el.no
4342deType === 1 ? el : el.parentElement); 4343 while (node && node !== document.body) { 4344 if (node.hasAttribute && node.hasAttribute("data-asc-page-type")) { 4345 return normalizePageType(node.getAttribute("data-asc-page-type")); 4346 } 4347 node = node.parentElement; 4348 } 4349 return normalizePageType( 4350 window.asc_datalayer && window.asc_datalayer.page_type 4351 ); 4352 } 4353 4354 function kebabToParam(key) { 4355 if (ATTR_TO_PARAM[key]) { 4356 return ATTR_TO_PARAM[key]; 4357 } 4358 return key.replace(/-/g, "_"); 4359 } 4360 4361 function collectDataAscAttributes(el) { 4362 var $el = $(el); 4363 var params = {}; 4364 4365 var title = 4366 $el.attr("data-asc-title") || 4367 $el.attr("data-asc-element-title") || 4368 $el.data("ascElementTitle"); 4369 if (title) { 4370 params.element_title = String(title).trim(); 4371 } 4372 4373 if (!el.attributes) { 4374 return params; 4375 } 4376 4377 for (var i = 0; i < el.attributes.length; i++) { 4378 var attr = el.attributes[i]; 4379 if (attr.name.indexOf("data-asc-") !== 0) { 4380 continue; 4381 } 4382 var key = attr.name.slice("data-asc-".length); 4383 if (key === "page-type") { 4384 continue; 4385 } 4386 var param = kebabToParam(key); 4387 if (attr.value !== undefined && attr.value !== "") { 4388 params[param] = attr.value; 4389 } 4390 } 4391 4392 return params; 4393 } 4394 4395 function getElementText($el) { 4396 var aria = $el.attr("aria-label"); 4397 if (aria && String(aria).trim()) { 4398 return String(aria).trim(); 4399 } 4400 if ($el.is("input[type=submit], input[type=button], button")) { 4401 return ($el.val() || $el.text() || "").trim(); 4402 } 4403 if ($el.is("img")) { 4404 return ($el.attr("alt") || "").trim(); 4405 } 4406 if ($el.is("a")) { 4407 return modules.utilities.getAggregateText($el) || ""; 4408 } 4409 return modules.utilities.getInnerText($el) || ""; 4410 } 4411 4412 function normalizeCommPhone($element) { 4413 var hrefRaw = ($element.attr("href") || "").replace(/^tel:/i, "").trim(); 4414 var fromHref = modules.utilities.phoneToE164(hrefRaw); 4415 if (fromHref) { 4416 return fromHref; 4417 } 4418 var dataComm = $element.attr("data-comm-phone-number"); 4419 if (dataComm) { 4420 var fromData = modules.utilities.phoneToE164(dataComm); 4421 if (fromData) { 4422 return fromData; 4423 } 4424 } 4425 var text = modules.utilities.getInnerText($element); 4426 return modules.utilities.phoneToE164(text) || text || ""; 4427 } 4428 4429 function isTelElement(el) { 4430 var href = (el && el.href) || $(el).attr("href") || ""; 4431 return /^tel:/i.test(href); 4432 } 4433 4434 function resolveDecoratedLinkUrl($el, clickTarget) { 4435 if ($el[0] && $el[0].href) { 4436 return $el[0].href; 4437 } 4438 if (clickTarget && clickTarget.closest) { 4439 var anchor = clickTarget.closest("a"); 4440 if (anchor && anchor.href) { 4441 return anchor.href; 4442 } 4443 } 4444 if (clickTarget && clickTarget.href) { 4445 return clickTarget.href; 4446 } 4447 return null; 4448 } 4449 4450 function routeDecoratedEvent(ascEventData) { 4451 if (ascEventData.event !== ASC.EVENT.ASC_CTA_INTERACTION) { 4452 return ascEventData; 4453 } 4454 if (ascEventData.element_type === ASC.ELEMENT_TYPE.MENU) { 4455 ascEventData.event = ASC.EVENT.ASC_MENU_INTERACTION; 4456 } else if ( 4457 ascEventData.element_type === ASC.ELEMENT_TYPE.GALLERY || 4458 ascEventData.element_type === ASC.ELEMENT_TYPE.VIDEO || 4459 ascEventData.media_type 4460 ) { 4461 ascEventData.event = ASC.EVENT.ASC_MEDIA_INTERACTION; 4462 } else if (ascEventData.element_type === ASC.ELEMENT_TYPE.FORM) { 4463 ascEventData.event = ASC.EVENT.ASC_FORM_ENGAGEMENT; 4464 } else if ( 4465 ascEventData.element_type === ASC.ELEMENT_TYPE.CHAT_TOOL || 4466 ascEventData.comm_type 4467 ) { 4468 ascEventData.event = ASC.EVENT.ASC_COMM_ENGAGEMENT; 4469 } else if ( 4470 ascEventData.element_state || 4471 ascEventData.element_type === ASC.ELEMENT_TYPE.PAGINATION || 4472 ascEventData.element_type === ASC.ELEMENT_TYPE.SEARCH_TOOL 4473 ) { 4474 ascEventData.event = ASC.EVENT.ASC_ELEMENT_CONFIGURATION; 4475 } 4476 return ascEventData; 4477 } 4478 4479 function buildAscPayloadFromElement($el, eventName, clickTarget) { 4480 var attrParams = collectDataAscAttributes($el[0]); 4481 var $click = $(clickTarget || $el[0]); 4482 var explicitSubtype = ($el.attr("data-asc-subtype") || "").trim(); 4483 4484 var ascEventData = { 4485 event: eventName, 4486 event_owner: window.asc_datalayer.affiliation, 4487 page_type: resolvePageType($el[0]), 4488 event_action: ASC.EVENT_ACTION.CLICK, 4489 event_action_result: ASC.EVENT_ACTION_RESULT.OPEN, 4490 element_text: getElementText($el), 4491 element_type: 4492 attrParams.element_type || ASC.ELEMENT_TYPE.ITEM_DETAILS, 4493 element_subtype: 4494 explicitSubtype || 4495 attrParams.element_subtype || 4496 ASC.ELEMENT_SUBTYPE.CTA_BUTTON, 4497 }; 4498
4499 Object.keys(attrParams).forEach(function (key) { 4500 ascEventData[key] = attrParams[key]; 4501 }); 4502 4503 if (!ascEventData.element_title && ascEventData.element_text) { 4504 ascEventData.element_title = ascEventData.element_text; 4505 } 4506 4507 var href = resolveDecoratedLinkUrl($el, clickTarget); 4508 if (href) { 4509 ascEventData.link_url = href; 4510 } 4511 4512 if (isTelElement(clickTarget || $el[0])) { 4513 ascEventData.event = ASC.EVENT.ASC_CLICK_TO_CALL; 4514 ascEventData.comm_phone_number = normalizeCommPhone($click); 4515 ascEventData.department = 4516 ascEventData.department || ASC.DEPARTMENT.SALES; 4517 if (!ascEventData.element_text && ascEventData.comm_phone_number) { 4518 ascEventData.element_text = ascEventData.comm_phone_number; 4519 } 4520 } 4521 4522 return routeDecoratedEvent(ascEventData); 4523 } 4524 4525 function trackDecoratedClick(currentTarget) { 4526 if (!AscTracking || !window.asc_datalayer) { 4527 return false; 4528 } 4529 4530 var decorated = findAscTarget(currentTarget); 4531 if (!decorated) { 4532 return false; 4533 } 4534 4535 var $el = $(decorated); 4536 var trackAsc = ($el.attr("data-track-asc") || "").trim(); 4537 if (!trackAsc) { 4538 return false; 4539 } 4540 4541 var eventNames = trackAsc 4542 .split(",") 4543 .map(function (name) { 4544 return name.trim(); 4545 }) 4546 .filter(Boolean); 4547 4548 eventNames.forEach(function (eventName) { 4549 var payload = buildAscPayloadFromElement( 4550 $el, 4551 eventName, 4552 currentTarget 4553 ); 4554 AscTracking.fireAscEvent(payload); 4555 }); 4556 4557 return true; 4558 } 4559 4560 function getFormContext($form) { 4561 var $f = $form instanceof jQuery ? $form : $($form); 4562 return { 4563 form_name: 4564 $f.attr("data-asc-form-name") || 4565 $f.find(".wts-custom-form-name").val() || 4566 window.customPageName || 4567 "", 4568 form_type: $f.attr("data-asc-form-type") || ASC.FORM_TYPE.CUSTOM, 4569 department: $f.attr("data-asc-department") || ASC.DEPARTMENT.SALES, 4570 page_type: resolvePageType($f[0]), 4571 }; 4572 } 4573 4574 function getCustomFormFieldLabel($element) { 4575 return getFormFieldVisibleLabel($element); 4576 } 4577 4578 function getFormFieldObjectTitle($element) { 4579 var $el = $element instanceof jQuery ? $element : $($element); 4580 return ( 4581 $el.attr("data-asc-title") || 4582 $el.attr("data-asc-element-title") || 4583 $el.data("ascElementTitle") || 4584 ($el.data("label") && String($el.data("label")).trim()) || 4585 "" 4586 ); 4587 } 4588 4589 function getFormFieldVisibleLabel($element) { 4590 var $el = $element instanceof jQuery ? $element : $($element); 4591 var $group = $el.closest(".form-group"); 4592 var labelText = $group 4593 .find(".wts-custom-form-label") 4594 .first() 4595 .text() 4596 .replace(/\*/g, "") 4597 .trim(); 4598 if (labelText) { 4599 return labelText; 4600 } 4601 var siblingLabel = $el 4602 .siblings("label") 4603 .text() 4604 .replace(/\*/g, "") 4605 .trim(); 4606 if (siblingLabel) { 4607 return siblingLabel; 4608 } 4609 return ($el.data("label") && String($el.data("label")).trim()) || ""; 4610 } 4611 4612 function isPiiField($el) { 4613 var name = ($el.attr("name") || $el.attr("id") || "").trim(); 4614 if (!name) { 4615 return false; 4616 } 4617 if (PII_FIELD_NAMES[name]) { 4618 return true; 4619 } 4620 var lower = name.toLowerCase(); 4621 return ( 4622 lower.indexOf("email") >= 0 || 4623 lower.indexOf("phone") >= 0 || 4624 lower === "name" || 4625 lower.indexOf("firstname") >= 0 || 4626 lower.indexOf("lastname") >= 0 4627 ); 4628 } 4629 4630 function getFormFieldElementTitle($element) { 4631 return getFormFieldObjectTitle($element); 4632 } 4633 4634 function getFormFieldDisplayValue($element) { 4635 var $el = $element instanceof jQuery ? $element : $($element); 4636 var type = ($el.attr("type") || "").toLowerCase(); 4637 4638 if (type === "checkbox") { 4639 return $el.prop("checked") ? "checked" : "unchecked"; 4640 } 4641 if (type === "radio") { 4642 if (!$el.prop("checked")) { 4643 return ""; 4644 } 4645 return ($el.val() || "checked").trim(); 4646 }
4647 if ($el.is("select")) { 4648 var selected = $el.find("option:selected"); 4649 return (selected.text() || selected.val() || "").trim(); 4650 } 4651 return ($el.val() || "").trim(); 4652 } 4653 4654 function getFormFieldElementText($element) { 4655 return getFormFieldVisibleLabel($element); 4656 } 4657 4658 function getFormFieldElementValue($element) { 4659 var $el = $element instanceof jQuery ? $element : $($element); 4660 var displayValue = getFormFieldDisplayValue($el); 4661 if (!displayValue) { 4662 return ""; 4663 } 4664 if (isPiiField($el)) { 4665 return "entered"; 4666 } 4667 return displayValue; 4668 } 4669 4670 function resolveSecondarySubmissionEvent(department, formType) { 4671 if (formType === ASC.FORM_TYPE.SERVICE_APPOINTMENT) { 4672 return ASC.EVENT.ASC_FORM_SUBMISSION_APPT; 4673 } 4674 if (department === ASC.DEPARTMENT.PARTS || formType === ASC.FORM_TYPE.PARTS) { 4675 return ASC.EVENT.ASC_FORM_SUBMISSION_PARTS; 4676 } 4677 if (department === ASC.DEPARTMENT.SERVICE) { 4678 return ASC.EVENT.ASC_FORM_SUBMISSION_SERVICE; 4679 } 4680 if (department === ASC.DEPARTMENT.SALES) { 4681 return ASC.EVENT.ASC_FORM_SUBMISSION_SALES; 4682 } 4683 if ( 4684 formType === ASC.FORM_TYPE.CONSUMER_CONTACT || 4685 formType === ASC.FORM_TYPE.CUSTOM || 4686 formType === ASC.FORM_TYPE.QUOTE || 4687 formType === ASC.FORM_TYPE.OFFER || 4688 formType === ASC.FORM_TYPE["PRE-ORDER"] 4689 ) { 4690 return ASC.EVENT.ASC_FORM_SUBMISSION_SALES; 4691 } 4692 return ASC.EVENT.ASC_FORM_SUBMISSION_OTHER; 4693 } 4694 4695 modules.AscDelegatedTracking = { 4696 findAscTarget: findAscTarget, 4697 resolvePageType: resolvePageType, 4698 buildAscPayloadFromElement: buildAscPayloadFromElement, 4699 trackDecoratedClick: trackDecoratedClick, 4700 getFormContext: getFormContext, 4701 getCustomFormFieldLabel: getCustomFormFieldLabel, 4702 getFormFieldElementTitle: getFormFieldElementTitle, 4703 getFormFieldDisplayValue: getFormFieldDisplayValue, 4704 getFormFieldElementText: getFormFieldElementText, 4705 getFormFieldElementValue: getFormFieldElementValue, 4706 resolveSecondarySubmissionEvent: resolveSecondarySubmissionEvent, 4707 }; 4708})(window.wts, window.wts.modules); 4709; 4710(function (wts, modules) { 4711 // imports 4712 var getClientData = modules.getClientData; 4713 var utilities = modules.utilities; 4714 4715 // initialization of the module 4716 var $adobeAnalyticsSetup = getClientData("#adobe-analytics-setup"); 4717 4718 // "private static" members 4719 var _vocationalCarouselCurrentPage = 0; 4720 var _formInteractions = new Map(); 4721 // Guard against null setup data (e.g., when Ford Direct Analytics is active) 4722 var _isEnabled = $adobeAnalyticsSetup && $adobeAnalyticsSetup.isEnabled; 4723 4724 // "public static" functions 4725 const AdobeAnalytics = { 4726 /** 4727 * Gets common data that is embedded within the page and other places for interaction clicks. Used for Adobe Analytics. 4728 * @param event Contains context around the event that was fired. 4729 * @returns An object containing data necessary for tracking interaction clicks or null if the module is disabled. 4730 */ 4731 getAdobeInteractionClickEventData: function (event) { 4732 if (!_isEnabled) { 4733 return null; 4734 } 4735 4736 const siteDelimiter = window.location.hostname.indexOf("."); 4737 const site = 4738 siteDelimiter === -1 4739 ? window.location.hostname 4740 : window.location.hostname.substring(0, siteDelimiter); 4741 4742 var adobeInteractionClickData = { 4743 interaction: { 4744 site: site, 4745 type: event.data.type, 4746 page: 4747 event.data.pageName || 4748 `shop:${resolveStellantisPageName()}`, 4749 location: event.data.location, 4750 description: event.data.description, 4751 }, 4752 }; 4753 4754 if (!event.data.hideVehicle) { 4755 const marketingData = 4756 getClientData(
4757 `#analytics-marketing-data-${event.data.truckID}` 4758 ) || getClientData("#marketing-events-data"); 4759 if (marketingData) { 4760 adobeInteractionClickData.interaction.description = 4761 event.data.description.replace( 4762 "<vehicleModel>", 4763 marketingData.model 4764 ); 4765 adobeInteractionClickData.vehicle = { 4766 make: marketingData.make, 4767 model: marketingData.model, 4768 year: marketingData.year, 4769 trim: marketingData.trim, 4770 vin: marketingData.vin, 4771 }; 4772 4773 if (marketingData.vin) { 4774 adobeInteractionClickData.vehicle.vin = 4775 marketingData.vin; 4776 } 4777 } else if (event.data.vin) { 4778 adobeInteractionClickData.vehicle = { 4779 vin: event.data.vin, 4780 }; 4781 } 4782 } 4783 4784 return adobeInteractionClickData; 4785 }, 4786 4787 /** 4788 * Gets common data necessary for tracking adobe form start click events. 4789 * @param event Contains context around the event that was fired. 4790 * @returns An object containing data necessary for tracking form start clicks or null if the module is disabled. 4791 */ 4792 getAdobeFormStartClickEventData: function (event) { 4793 if (!_isEnabled) { 4794 return null; 4795 } 4796 4797 var adobeFormStartClickEventData = { 4798 form: { 4799 formDescription: event.data.formDescription, 4800 formType: event.data.formType, 4801 displayType: event.data.displayType, 4802 displayFormat: event.data.displayFormat, 4803 }, 4804 }; 4805 4806 const marketingData = 4807 getClientData( 4808 `#analytics-marketing-data-${event.data.truckID}` 4809 ) || getClientData("#marketing-events-data"); 4810 if (marketingData) { 4811 adobeFormStartClickEventData.vehicle = { 4812 make: marketingData.make, 4813 model: marketingData.model, 4814 year: marketingData.year, 4815 trim: marketingData.trim, 4816 vin: marketingData.vin, 4817 }; 4818 } 4819 4820 return adobeFormStartClickEventData; 4821 }, 4822 4823 /** 4824 * Gets common data necessary for tracking adobe form submit events. 4825 * @param event Contains context around the event that was fired. 4826 * @returns An object containing data necessary for tracking form submit clicks or null if the module is disabled. 4827 */ 4828 getAdobeFormSubmitData: function (event) { 4829 if (!_isEnabled) { 4830 return null; 4831 } 4832 4833 var adobeFormSubmitData = { 4834 form: { 4835 formDescription: event.data.formDescription, 4836 formType: event.data.formType, 4837 displayType: event.data.displayType, 4838 displayFormat: event.data.displayFormat, 4839 hashedEmail: event.data.hashedEmail, 4840 leadId: event.data.uniqueID, 4841 }, 4842 }; 4843 4844 const marketingData = 4845 getClientData( 4846 `#analytics-marketing-data-${event.data.truckID}` 4847 ) || getClientData("#marketing-events-data"); 4848 if (marketingData) { 4849 adobeFormSubmitData.vehicle = { 4850 make: marketingData.make, 4851 model: marketingData.model, 4852 year: marketingData.year, 4853 trim: marketingData.trim, 4854 vin: marketingData.vin, 4855 }; 4856 } 4857 4858 return adobeFormSubmitData; 4859 }, 4860 4861 /**
4862 * Builds an eventData object containing interaction click event data based on information from a General Layout's currentTarget. 4863 * @param {any} currentTarget The General Layout target that fired the event. 4864 * @returns null if the event isn't handled or the module is disabled OR the built eventData object. 4865 */ 4866 getAdobeEventDataFromGeneralLayoutTarget: function (currentTarget) { 4867 if (!_isEnabled) { 4868 return null; 4869 } 4870 4871 var $element = $(currentTarget); 4872 var eventData = { 4873 truckID: null, 4874 type: null, 4875 location: null, 4876 description: null, 4877 pageName: null, 4878 }; 4879 4880 if (currentTarget.href) { 4881 // handle all links: i.e. link back to dealer site, links to the VDP, or links in the footer 4882 eventData.truckID = modules.utilities.getTruckID($element); 4883 4884 // test to see if this is a navigation link which is different than the links within the inventory grid 4885 var parentElement = $element.parents(".navigation__link"); 4886 if (!parentElement.length) { 4887 // if not check if the link was present in the footer 4888 parentElement = $element.parents(".footer-nav").first(); 4889 } 4890 4891 if (parentElement && parentElement.attr("class")) { 4892 // header/footer link 4893 eventData.type = "nav"; 4894 eventData.location = parentElement.hasClass("footer-nav") 4895 ? "footer" 4896 : "header"; 4897 eventData.description = $element 4898 .text() 4899 .trim() 4900 .replace(/\s+/g, "-") 4901 .toLowerCase(); 4902 } else if ($element.hasClass("specifications__see-more")) { 4903 // see more details link click to open the VDP from VSP 4904 eventData.type = "cta"; 4905 eventData.location = "vehicle-details"; 4906 eventData.description = "see-more-details-<vehicleModel>"; 4907 } else if ($element.hasClass("truck-image")) { 4908 // truck image click to open the VDP from VSP 4909 eventData.type = "cta"; 4910 eventData.location = "select-vehicle"; 4911 eventData.description = "truck-image-<vehicleModel>"; 4912 } else if ($element.hasClass("truck__link-text")) { 4913 // truck title click to open the VDP from VSP 4914 eventData.type = "cta"; 4915 eventData.location = "vehicle-details"; 4916 eventData.description = "truck-title-<vehicleModel>"; 4917 } else if ($element.hasClass("truck__phone-link")) { 4918 // phone number click within the VSP inventory grid item 4919 eventData.type = "cta"; 4920 eventData.pageName = "landing"; 4921 eventData.location = "select-vehicle"; 4922 eventData.description = "call-phone-number-<vehicleModel>"; 4923 } else if ($element.hasClass("call-us-now-phone")) { 4924 // phone number click at bottom of I'm Interested form 4925 eventData.truckID = $(".hidden-inventory-id").val(); 4926 eventData.type = "cta"; 4927 eventData.pageName = "landing"; 4928 eventData.location = "select-vehicle"; 4929 eventData.description = "call-us-now-<vehicleModel>"; 4930 } else if ( 4931 $element.hasClass("advanced-pricing-actions__phone-link") 4932 ) { 4933 // phone number click directly on VDP 4934 eventData.type = "cta"; 4935 eventData.location = "vehicle-container"; 4936 eventData.description = "call-phone-number-<vehicleModel>"; 4937 } else if ($element.hasClass("photo-gallery__open-image")) { 4938 // open in new tab click from photo gallery 4939 eventData.type = "func"; 4940 eventData.location = "thumbnail-gallery"; 4941 eventData.description = "open-in-new-tab"; 4942 } else if (
4943 $element.hasClass("wts-vehicle-carouse__lite-item-link") 4944 ) { 4945 eventData.type = "slider"; 4946 eventData.location = "carousel-page-bottom"; 4947 eventData.truckID = $element.data("truckId"); 4948 4949 const descriptionPrefix = 4950 modules.utilities.getCarouselDescription($element); 4951 eventData.description = `${descriptionPrefix}-${$element.data( 4952 "slideNumber" 4953 )}`; 4954 } else { 4955 // check if it is a link in a custom header (typically a "Back to somewhere.com" link) 4956 const parentDiv = 4957 $element.parents(".custom-header") || 4958 $element.parents(".custom-secondary-header"); 4959 4960 if (parentDiv) { 4961 eventData.type = "nav"; 4962 eventData.location = "topnav"; 4963 eventData.description = $element 4964 .text() 4965 .trim() 4966 .replace(/\s+/g, "-") 4967 .toLowerCase(); 4968 } else { 4969 // presently an unhandled event so return null which means don't fire an event 4970 return null; 4971 } 4972 } 4973 } else if ($element.hasClass("facet-range__input")) { 4974 // handle facet range changes (i.e. Year, Cab to Axle, GVRW) 4975 eventData.type = "tool"; 4976 4977 const $container = $element.closest(".facet-range--container"); 4978 eventData.location = $container 4979 .data("key") 4980 .trim() 4981 .replace(/\s+/g, "-") 4982 .toLowerCase(); 4983 eventData.description = `${$container 4984 .find(".facet-range__input") 4985 .first() 4986 .val()}-${$container 4987 .find(".facet-range__input") 4988 .last() 4989 .val()}`; 4990 } else if ($element.hasClass("facet")) { 4991 // handle normal facet clicks 4992 eventData.type = "tool"; 4993 4994 // get the title for the facet, trim it, replace any spaces with hyphens, and lowercase that puppy 4995 const headingTitle = $element 4996 .closest(".facet-filters") 4997 .find(".collapsible__title") 4998 .html() 4999 ?.trim() 5000 .replace(/\s+/g, "-") 5001 .toLowerCase(); 5002 eventData.location = headingTitle; 5003 5004 eventData.description = `${window.wts.modules.utilities 5005 .getInnerText($element) 5006 .toLowerCase()}-${ 5007 $element.hasClass("checked") ? "checked" : "unchecked" 5008 }`; 5009 } else if ($element.hasClass("pagination__list-item")) { 5010 // handle inventory grid pagination previous/next clicks 5011 eventData.type = "func"; 5012 eventData.location = "bottomnav"; 5013 5014 var description; 5015 const event = $element.data("event"); 5016 5017 if (event === "Go To Page") { 5018 description = `pagination-${$element.data("value")}`; 5019 } else { 5020 description = 5021 event.indexOf("Next") > -1 5022 ? "pagination-next" 5023 : "pagination-prev"; 5024 } 5025 5026 eventData.description = description; 5027 } else if ( 5028 $element.hasClass( 5029 "wts-vehicle-carousel__navigation-action--left" 5030 ) 5031 ) { 5032 // handle similar/recent carousel previous clicks 5033 eventData.type = "slider"; 5034 eventData.location = "carousel-page-bottom"; 5035 eventData.hideVehicle = true; 5036 5037 const descriptionPrefix = 5038 modules.utilities.getCarouselDescription($element); 5039 eventData.description = `${descriptionPrefix}-previous`; 5040 } else if ( 5041 $element.hasClass(
5042 "wts-vehicle-carousel__navigation-action--right" 5043 ) 5044 ) { 5045 // handle similar/recent carousel next clicks 5046 eventData.type = "slider"; 5047 eventData.location = "carousel-page-bottom"; 5048 eventData.hideVehicle = true; 5049 5050 const descriptionPrefix = 5051 modules.utilities.getCarouselDescription($element); 5052 eventData.description = `${descriptionPrefix}-next`; 5053 } else if ( 5054 $element.hasClass("wts-vehicle-carouse__lite-item-link") 5055 ) { 5056 // handle clicking a vehicle tile within the similar/recent carousels 5057 eventData.type = "slider"; 5058 eventData.location = "carousel-page-bottom"; 5059 eventData.description = `${modules.utilities.getCarouselDescription( 5060 $element 5061 )}-tile`; 5062 } else if ($element.hasClass("truck-actions__interested")) { 5063 // handle I'm Interested clicks from the VSP 5064 eventData.truckID = modules.utilities.getTruckID($element); 5065 eventData.type = "cta"; 5066 eventData.pageName = "landing"; 5067 eventData.location = "select-vehicle"; 5068 eventData.description = "contact-your-dealer-<vehicleModel>"; 5069 } else if ($element.hasClass("im-interested__action")) { 5070 // handle I'm Interested clicks from the VDP 5071 eventData.type = "cta"; 5072 eventData.location = "vehicle-container"; 5073 eventData.description = "Interested-<vehicleModel>"; 5074 } else if ( 5075 $element.hasClass("featured-photo-gallery__see-all-photos") 5076 ) { 5077 // handle See All Photos click from the VDP 5078 eventData.type = "tool"; 5079 eventData.location = "vehicle-container"; 5080 eventData.description = "gallery-seeAllPhotos"; 5081 } else { 5082 // presently an unhandled event so return null which means don't fire an event 5083 return null; 5084 } 5085 5086 return { data: eventData }; 5087 }, 5088 5089 /** 5090 * Builds an eventData object based on information from currentTarget which is a specific pagination control. 5091 * @param {any} currentTarget The specific pagination control that fired the event. 5092 * @returns An eventData object containing information derived from the pagination control that fired the event or null if the module is disabled. 5093 */ 5094 getAdobeEventDataFromPaginationControls: function (currentTarget) { 5095 if (!_isEnabled) { 5096 return null; 5097 } 5098 5099 const $element = $(currentTarget); 5100 var eventData = { 5101 type: "func", 5102 location: "", 5103 description: "", 5104 }; 5105 5106 // determine if the control is located in the top or bottom nav as well as which control is being interacted with 5107 const $parentElement = $element.parents(".pagination-top"); 5108 const location = $parentElement.length ? "topnav" : "bottomnav"; 5109 eventData.location = location; 5110 5111 const selectedText = $element 5112 .find(":selected") 5113 .text() 5114 .replace(/\s+/g, "-") 5115 .replace(/\(/g, "") 5116 .replace(/\)/g, "") 5117 .toLowerCase(); 5118 eventData.description = `${ 5119 $element.hasClass("items-per-page__input") 5120 ? "items-per-page" 5121 : "sort-by" 5122 }-${selectedText}`; 5123 5124 return { data: eventData }; 5125 }, 5126 5127 /** 5128 * Builds an eventData object based on information from pageChangeEventData. 5129 * @param {any} pageChangeEventData The specific pageChangeEventData to build the eventData from.
5130 * @returns An eventData object containing information derived from the pageChangeEventData or null if the module is disabled. 5131 */ 5132 getAdobeEventDataFromVocationalPageChange: function ( 5133 pageChangeEventData 5134 ) { 5135 if (!_isEnabled) { 5136 return null; 5137 } 5138 5139 var eventData = { 5140 type: "filter", 5141 location: "main-filter-carousel", 5142 description: "", 5143 }; 5144 const pageNumber = pageChangeEventData.page.index; 5145 const pageSize = pageChangeEventData.page.size; 5146 const pageCount = pageChangeEventData.page.count; 5147 5148 // determine if the user clicked next or previous arrow 5149 var direction; 5150 if (_vocationalCarouselCurrentPage === 0) { 5151 if (pageNumber === pageSize) { 5152 direction = "previous"; 5153 _vocationalCarouselCurrentPage = pageSize; 5154 } else { 5155 direction = "next"; 5156 _vocationalCarouselCurrentPage = pageNumber; 5157 } 5158 } else { 5159 direction = 5160 _vocationalCarouselCurrentPage <= pageNumber 5161 ? "next" 5162 : _vocationalCarouselCurrentPage === pageCount - 1 && 5163 pageNumber === 0 5164 ? "next" 5165 : "previous"; 5166 _vocationalCarouselCurrentPage = pageNumber; 5167 } 5168 5169 eventData.description = `filter-${pageNumber + 1}:${direction}`; 5170 return { data: eventData }; 5171 }, 5172 5173 /** 5174 * Builds an eventData object based on form control information derived from currentTarget. 5175 * @param {any} currentTarget The specific element inside a form that fired the event. 5176 * @returns null if the parentFormID cannot be derived or if the form has already been interacted with or the module is disabled. 5177 * An eventData object containing information derived from currentTarget otherwise. 5178 */ 5179 getAdobeEventDataFromFormStart: function (currentTarget) { 5180 if (!_isEnabled) { 5181 return null; 5182 } 5183 5184 const $element = $(currentTarget); 5185 const parentFormID = $element 5186 .parents(".im-interested-form") 5187 .attr("id"); 5188 5189 if (!parentFormID) { 5190 return null; 5191 } 5192 5193 var formInteraction = _formInteractions.get(parentFormID); 5194 if (formInteraction) { 5195 // the user has already interacted with the form so don't fire an event 5196 return null; 5197 } 5198 5199 formInteraction = new Map(); 5200 _formInteractions.set(parentFormID, formInteraction); 5201 5202 var eventData = { 5203 truckID: $(".hidden-inventory-id").val(), 5204 formDescription: "lead", 5205 formType: "ram-wts-form", 5206 displayType: "page", 5207 displayFormat: "in-page", 5208 }; 5209 return { data: eventData }; 5210 }, 5211 5212 /** 5213 * Gets common data that is embedded within the page and other places for page loads. Used for Adobe Analytics. 5214 * @param event Contains context around the event that was fired. 5215 * @returns An object containing data necessary for tracking page loads or null if the module is disabled. 5216 */ 5217 getAdobePageLoadEventData: function (event) { 5218 if (!_isEnabled) { 5219 return null; 5220 } 5221 5222 const userData = getClientData("#user-data"); 5223 const location = window.wts.user.location.get(); 5224 const pageName = resolveStellantisPageName(); 5225 var adobePageLoadData = { 5226 platform: { 5227 technology: ".NET", 5228 }, 5229 user: { 5230 language: userData.language, 5231 responsiveState: detectDeviceType(), 5232 zipCode: location ? location.ZipCode : "", 5233 }, 5234 page: { 5235 name: `shop:${pageName}`, 5236 type: pageName, 5237 }, 5238 }; 5239 5240 const marketingEventsData = getClientData("#marketing-events-data"); 5241 if (marketingEventsData) { 5242 adobePageLoadData.vehicle = { 5243 make: marketingEventsData.make, 5244 model: marketingEventsData.model, 5245 year: marketingEventsData.year, 5246 trim: marketingEventsData.trim, 5247 vin: marketingEventsData.vin, 5248 }; 5249 } 5250 5251 const siteData = getClientData("#site-data"); 5252 adobePageLoadData.dealer = { 5253 code: siteData.DealerCode, 5254 }; 5255 5256 return adobePageLoadData; 5257 }, 5258 }; 5259 5260 // "private static" functions 5261 5262 /** 5263 * Detects the device type by observing screen width. 5264 * Uses the shared utility from modules.utilities.
5265 * @returns "desktop", "tablet", or "mobile" depending on the screen width of the window. 5266 */ 5267 function detectDeviceType() { 5268 return utilities.detectDeviceType(); 5269 } 5270 5271 /** 5272 * Attempts to map the pageType from the page-context-data into a value Stellantis expects. 5273 * @returns the resolved pageName 5274 */ 5275 function resolveStellantisPageName() { 5276 const pageContextData = 5277 window.wts.modules.getClientData("#page-context-data"); 5278 if (!pageContextData) { 5279 return null; 5280 } 5281 5282 switch (pageContextData.pageType) { 5283 case "SearchResults": 5284 return "search-inventory"; 5285 case "TruckDetails": 5286 return "vehicle-details"; 5287 break; 5288 default: 5289 return "not-handled"; 5290 } 5291 } 5292 5293 // exports 5294 modules.AdobeAnalytics = AdobeAnalytics; 5295})(window.wts, window.wts.modules); 5296; 5297/** 5298 * Ford Direct Analytics Module 5299 * 5300 * Implements Ford Direct's Adobe DTM tagging requirements. 5301 * Builds window.digitaldata dynamically for ALL pages when enabled. 5302 * 5303 * This module is mutually exclusive with the existing Adobe Analytics (Stellantis) implementation. 5304 * When FordDirectAnalyticsScriptURL is set, this module handles analytics events. 5305 */ 5306(function (wts, modules) { 5307 // imports 5308 var getClientData = modules.getClientData; 5309 var utilities = modules.utilities; 5310 5311 // initialization of module - get setup data from JSON embedded in page 5312 var fordDirectSetup = getClientData("#ford-direct-analytics-setup"); 5313 var userData = getClientData("#user-data"); 5314 5315 // Check if user is TruckPro or above (staff/admin) - Ford Direct only tracks buyer-facing pages 5316 var isTruckProOrAbove = userData && userData.isTruckProOrAbove; 5317 5318 // Check if FDAF data is complete (required for Ford Direct user object) 5319 var hasFdafData = fordDirectSetup && fordDirectSetup.hasFdafData; 5320 5321 // Disable for TruckPro/Staff/Admin users - only track anonymous buyer-facing pages 5322 // Also disable if FDAF data is incomplete (Salesforce sync may not have completed) 5323 var _isEnabled = 5324 fordDirectSetup && fordDirectSetup.isEnabled && hasFdafData && !isTruckProOrAbove; 5325 5326 // Enable debug logging via ?debug-fd=1 query param 5327 var enableDebug = fordDirectSetup && fordDirectSetup.enableDebug; 5328 5329 // Store last PAGE_LOAD digitaldata and page context key for use in onclick events 5330 var lastPageLoadData = null; 5331 var lastPageContextKey = null; 5332 5333 // Open global-overlay (2c) entries, innermost last. Kept separate from lastPageLoadData so 5334 // form-submit keeps baselining against the real page load while an overlay is open. 5335 // Scoped to the document: navigation tears it down, and closeOverlay pops entries. It is 5336 // deliberately NOT reset in trackPageLoad, which re-fires on the live document for 5337 // VSP_SEARCH_COMPLETE and VDP print and would discard an overlay the user still has open. 5338 var activeOverlayStack = []; 5339 5340 if (isTruckProOrAbove && enableDebug) { 5341 console.log( 5342 "[Ford Direct Analytics] Disabled for TruckPro/Staff users" 5343 ); 5344 } 5345 5346 // Log warning if Ford Direct is configured but FDAF data is missing 5347 if (fordDirectSetup && fordDirectSetup.isEnabled && !hasFdafData && !isTruckProOrAbove) { 5348 var missingFields = []; 5349 if (!fordDirectSetup.paCode) missingFields.push("paCode"); 5350 if (!fordDirectSetup.fdafCode) missingFields.push("fdafCode"); 5351 if (!fordDirectSetup.fdafRegion) missingFields.push("fdafRegion"); 5352 console.warn( 5353 "[Ford Direct Analytics] Disabled - missing FDAF data:", 5354 missingFields.join(", "), 5355 "(Salesforce sync may not have completed)" 5356 ); 5357 } 5358 5359 // Page context mapping from VehicleDisplayContext to internal context keys 5360 var PAGE_CONTEXT_MAP = { 5361 None: "unknown", 5362 TruckDetails: "VDP", 5363 SearchResults: "VSP", 5364 MyGarage: "MyGarage", 5365 Sitemap: "Sitemap", 5366 About: "About", 5367 CustomOrders: "CustomOrders", 5368 CommercialCatalog: "CommercialCatalog", 5369 // Special contexts passed via event data or detected from URL 5370 Home: "Home", 5371 PrivacyPolicy: "PrivacyPolicy", 5372 Definitions: "Definitions", 5373 VDPPRINT: "VDPPRINT", 5374 CustomPage: "CustomPage", 5375 }; 5376 5377 // Page path suffix for pageName format: dc:wts:{pagePath} 5378 // Format from Ford spec - omit nameplate for list views 5379 var PAGE_PATH_MAP = { 5380 Home: "home", 5381 VSP: "si:vls", 5382 VDP: "si:vehicle details", // nameplate appended dynamically for VDP 5383 VDPPRINT: "si:vehicle details:print", // nameplate appended dynamically 5384 MyGarage: "my garage", 5385 Sitemap: "help:sitemap", 5386 About: "about", 5387 CustomOrders: "custom order", 5388 CommercialCatalog: "commercial catalog", 5389 PrivacyPolicy: "help:privacy", 5390 Definitions: "help:definitions", 5391 CustomPage: "custom", // page title appended dynamically 5392 }; 5393 5394 // Site section mapping based on page context (from Ford spec) 5395 var SITE_SECTION_MAP = { 5396 Home: "home", 5397 VSP: "search inventory", 5398 VDP: "search inventory", 5399 VDPPRINT: "search inventory", 5400 MyGarage: "my garage", 5401 Sitemap: "help", 5402 About: "about", 5403 CustomOrders: "custom order", 5404 CommercialCatalog: "commercial catalog", 5405 PrivacyPolicy: "help", 5406 Definitions: "help", 5407 CustomPage: "custom", 5408 }; 5409 5410 // Page context path mapping for dynamic onclick link names 5411 // Maps internal page context keys to their onclick path prefix per Ford spec 5412 var ONCLICK_PAGE_CONTEXT_MAP = { 5413 Home: "home", 5414 VSP: "si:vls", 5415 VDP: "si:vehicle details", 5416 VDPPRINT: "si:vehicle details", 5417 About: "about", 5418 CustomOrders: "custom order", 5419 CommercialCatalog: "commercial catalog", 5420 MyGarage: "my garage", 5421 Sitemap: "help", 5422 PrivacyPolicy: "help", 5423 Definitions: "help", 5424 CustomPage: "custom", 5425 }; 5426 5427 // Dynamic action mapping for page-context-aware onclick link names 5428 // These actions require both page context AND modal context
5429 // Format: wts:{pageContext}:{modalContext}:content:{action} 5430 // The page context is derived from lastPageLoadData at click time 5431 // The modal context is passed from the event source 5432 var DYNAMIC_ACTION_MAP = { 5433 // Call Us Now - requires modal context (call popup, i am interested, etc.) 5434 CALL_US_NOW_IM_INTERESTED: "click to call", 5435 }; 5436 5437 // Modal context path segments for Call Us Now events per Ford spec 5438 // The modal type determines the middle segment of the onclick link name 5439 // Quote modal contexts are absent here on purpose - their linknames come from 5440 // QUOTE_MODAL_ONCLICKS because Ford drops the ":content:" segment from c5 for those rows. 5441 var MODAL_CONTEXT_MAP = { 5442 "call-popup": "call popup", 5443 "i-am-interested": "i am interested", 5444 "see-all-photos-i-am-interested": "see all photos:i am interested", 5445 }; 5446 5447 // Milestone 3 form-submit: B&G contexts use build-and-get-quote prefix even on VDP 5448 var BUILD_AND_GET_QUOTE_FORM_CONTEXTS = { 5449 "info:select upfit": true, 5450 "request more info": true, 5451 "get a quote for this build": true, 5452 }; 5453 5454 var FORM_SUBMIT_PREFIX_BY_PAGE_GROUP = { 5455 VSP: { 5456 pageNamePrefix: "si:vls", 5457 leadPrefix: "vls", 5458 siteSection: "search inventory", 5459 pageType: "search inventory", 5460 }, 5461 VDP: { 5462 pageNamePrefix: "si:vehicle details", 5463 leadPrefix: "vehicle details", 5464 siteSection: "search inventory", 5465 pageType: "search inventory", 5466 }, 5467 About: { 5468 pageNamePrefix: "about", 5469 leadPrefix: "about", 5470 siteSection: "about", 5471 pageType: "about", 5472 }, 5473 BuildAndGetQuote: { 5474 pageNamePrefix: "build and get quote", 5475 leadPrefix: "build and get quote", 5476 siteSection: "build and get quote", 5477 pageType: "build and get quote", 5478 }, 5479 CustomOrders: { 5480 pageNamePrefix: "custom order", 5481 leadPrefix: "custom order", 5482 siteSection: "custom order", 5483 pageType: "custom order", 5484 }, 5485 }; 5486 5487 var FORD_USER_LANGUAGE = "eng"; 5488 var FORD_CLIENT = "ford"; 5489 5490 // VDP content-action onclick events (Page Variables row 83) use distinct c5 vs pev2 values 5491 var VDP_CONTENT_ACTION_EVENT_KEYS = { 5492 PHOTO_GALLERY_NEXT_PHOTO: true, 5493 PHOTO_GALLERY_PREVIOUS_PHOTO: true, 5494 PHOTO_GALLERY_CLICK_THUMBNAIL: true, 5495 FEATURED_PHOTO_GALLERY_NEXT_PHOTO: true, 5496 FEATURED_PHOTO_GALLERY_PREVIOUS_PHOTO: true, 5497 FEATURED_PHOTO_GALLERY_VIEW_PHOTO: true, 5498 CLICK_FEATURED_PHOTO_THUMB: true, 5499 VEHICLE_DETAILS_TRACK_DEALER_VIDEO_EVENT: true, 5500 }; 5501 5502 // Onclick link name mapping for Ford Direct onclick events 5503 // Maps CTA element titles and event types to Ford spec onclickLinkName values 5504 // NOTE: Page-context-aware actions requiring modal context are in DYNAMIC_ACTION_MAP above 5505 var ONCLICK_LINK_NAME_MAP = { 5506 // Footer links (via GENERAL_LAYOUT_INTERACTION_CLICK + CTA element title) 5507 "Truck Pro Login": "wts:help:truck pro login", 5508 FAQs: "wts:help:faqs", 5509 "Learning Portal": "wts:help:faqs", 5510 Definitions: "wts:help:definitions", 5511 // Form submission 5512 SUBMIT_SUPPORT_FORM: "wts:help:support", 5513 // Photo/Video - per spec Row 83: wts:si:vehicle details:{action} 5514 PHOTO_GALLERY_NEXT_PHOTO: "wts:si:vehicle details:photo scroll", 5515 PHOTO_GALLERY_PREVIOUS_PHOTO: "wts:si:vehicle details:photo scroll", 5516 PHOTO_GALLERY_CLICK_THUMBNAIL: "wts:si:vehicle details:photo click", 5517 FEATURED_PHOTO_GALLERY_VIEW_PHOTO: "wts:si:vehicle details:photo click", 5518 VEHICLE_DETAILS_TRACK_DEALER_VIDEO_EVENT: "wts:si:vehicle details:video play", 5519 // Commercial catalog content actions - Page Variables row 77 5520 COMMERCIAL_CATALOG_VIEW_PHOTO: "wts:commercial catalog:photo click", 5521 COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_NEXT: 5522 "wts:commercial catalog:photo scroll", 5523 COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_PREVIOUS: 5524 "wts:commercial catalog:photo scroll", 5525 COMMERCIAL_CATALOG_VIEW_BODY_LINE_VIDEO: 5526 "wts:commercial catalog:video play", 5527 }; 5528 5529 // Commercial catalog onclick events sharing the single row 77 linkname. 5530 var CATALOG_CONTENT_ACTION_EVENT_KEYS = { 5531 COMMERCIAL_CATALOG_VIEW_PHOTO: true, 5532 COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_NEXT: true, 5533 COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_PREVIOUS: true, 5534 COMMERCIAL_CATALOG_VIEW_BODY_LINE_VIDEO: true, 5535 }; 5536 5537 // Call Us Now inside the Crestline quote forms - Page Variables rows 48 and 50. 5538 // These drop the ":content:" segment from c5 that other modal call flows use. 5539 var QUOTE_MODAL_ONCLICKS = { 5540 "get-quote": { 5541 c5: "wts:si:vehicle details:get quote:click to call", 5542 pev2: "dc:wts:si:vehicle details:get quote:content:action", 5543 }, 5544 "get-quote-vehicle-transportation": { 5545 c5: "wts:si:vehicle details:get quote:vehicle transportation quote:click to call", 5546 pev2: "dc:wts:si:vehicle details:get quote:vehicle transportation quote:content:action", 5547 }, 5548 }; 5549 5550 // window-sticker (4c) and global-download (7c). Both inherit channel and hierarchy from the 5551 // last page load and carry a tool/toolDescriptor pair plus an event action. 5552 var TOOL_EVENTS = { 5553 "window-sticker": { 5554 trackName: "window-sticker", 5555 c5: "wts:si:vehicle details:view window sticker", 5556 pev2: "dc:wts:si:vehicle details:view window sticker", 5557 appendNameplate: true, 5558 tool: "event:window sticker", 5559 toolDescriptor: "vehicle details:window sticker", 5560 eventAction: "window sticker|tool", 5561 }, 5562 "download-vehicle-details": { 5563 trackName: "global-download", 5564 c5: "wts:si:vehicle details:brochure download", 5565 pev2: "dc:wts:si:vehicle details:download", 5566 tool: "event:pdf brochure",
5567 toolDescriptor: "vehicle details:pdf brochure", 5568 eventAction: "pdf brochure|tool", 5569 }, 5570 "download-build-and-get-quote": { 5571 trackName: "global-download", 5572 c5: "wts:build and get quote:specs download", 5573 pev2: "dc:wts:build and get quote:download", 5574 tool: "event:pdf brochure", 5575 toolDescriptor: "build and get quote:pdf brochure", 5576 eventAction: "pdf brochure|tool", 5577 }, 5578 "download-commercial-catalog": { 5579 trackName: "global-download", 5580 c5: "wts:commercial catalog:brochure download", 5581 pev2: "dc:wts:commercial catalog:download", 5582 tool: "event:pdf brochure", 5583 toolDescriptor: "commercial catalog:pdf brochure", 5584 eventAction: "pdf brochure|tool", 5585 }, 5586 }; 5587 5588 // pev2 (onclickLinkName) where Ford spec requires a value distinct from c5. 5589 // Truck Pro Login is "dc:help:..." with no "wts" segment - Page Variables row 9 and the 5590 // 07/23 issue sheet agree on that, so it is not a typo. 5591 var ONCLICK_PEV2_MAP = { 5592 "Truck Pro Login": "dc:help:truck pro login", 5593 FAQs: "dc:wts:help:faqs", 5594 "Learning Portal": "dc:wts:help:faqs", 5595 Definitions: "dc:wts:help:definitions", 5596 SUBMIT_SUPPORT_FORM: "dc:wts:help:support", 5597 }; 5598 5599 // Page fields Ford requires an onclick to override rather than inherit from the last page load. 5600 var ONCLICK_PAGE_FIELD_OVERRIDES = { 5601 "Truck Pro Login": { 5602 siteSection: "help", 5603 hierarchy: "help:truck pro login", 5604 }, 5605 FAQs: { 5606 siteSection: "help", 5607 hierarchy: "help:support", 5608 pageNameNoVehicle: "dc:wts:help:faqs", 5609 }, 5610 "Learning Portal": { 5611 siteSection: "help", 5612 hierarchy: "help:support", 5613 pageNameNoVehicle: "dc:wts:help:faqs", 5614 }, 5615 Definitions: { 5616 siteSection: "help", 5617 hierarchy: "help:support", 5618 pageNameNoVehicle: "dc:wts:help:definitions", 5619 }, 5620 SUBMIT_SUPPORT_FORM: { 5621 siteSection: "help", 5622 hierarchy: "help:support", 5623 tool: "event:updates opt-in", 5624 toolDescriptor: "updates opt-in:global", 5625 }, 5626 }; 5627 5628 // Milestone 4 global-overlay (2c) definitions - 22 in-scope Page Variables rows. 5629 // pagePath is appended to "dc:wts:"; hierarchyBase gets ":{vehicle category}:{nameplate}" 5630 // appended when vehicleSuffix is true and a vehicle resolves. 5631 // Keys that differ per page group are nested under the group resolved from the last page load. 5632 // iaiFamily drives the hierarchy half of the onclick merge (issue sheet rows 26, 30, 36). 5633 var OVERLAY_CATALOG = { 5634 "call-popup": { 5635 VSP: { 5636 pagePath: "si:vls:call popup", 5637 siteSection: "search inventory", 5638 hierarchyBase: "shopping tools:call popup", 5639 vehicleSuffix: true, 5640 }, 5641 About: { 5642 pagePath: "about:call popup", 5643 siteSection: "about", 5644 hierarchyBase: "about:call popup", 5645 vehicleSuffix: true, 5646 }, 5647 }, 5648 "email-popup": { 5649 VSP: { 5650 pagePath: "si:vls:email popup", 5651 siteSection: "search inventory", 5652 hierarchyBase: "shopping tools:email popup", 5653 vehicleSuffix: true, 5654 }, 5655 About: { 5656 pagePath: "about:email popup", 5657 siteSection: "about", 5658 hierarchyBase: "about:email popup", 5659 vehicleSuffix: true, 5660 }, 5661 }, 5662 "i-am-interested": { 5663 iaiFamily: true, 5664 VSP: { 5665 pagePath: "si:vls:i am interested", 5666 siteSection: "search inventory", 5667 hierarchyBase: "shopping tools:i am interested", 5668 vehicleSuffix: true, 5669 }, 5670 VDP: { 5671 pagePath: "si:vehicle details:i am interested", 5672 siteSection: "search inventory", 5673 hierarchyBase: "shopping tools:i am interested", 5674 vehicleSuffix: true, 5675 }, 5676 }, 5677 "see-all-photos": { 5678 VSP: { 5679 pagePath: "si:vls:see all photos", 5680 siteSection: "search inventory", 5681 hierarchyBase: "shopping tools:see all photos", 5682 vehicleSuffix: true, 5683 }, 5684 VDP: { 5685 pagePath: "si:vehicle details:see all photos", 5686 siteSection: "search inventory", 5687 hierarchyBase: "shopping tools:see all photos", 5688 vehicleSuffix: true, 5689 }, 5690 // Same #photo-gallery-modal, different Ford row when opened from the catalog. 5691 CommercialCatalog: { 5692 pagePath: "commercial catalog:gallery popup", 5693 siteSection: "commercial catalog", 5694 hierarchyBase: "commercial catalog", 5695 vehicleSuffix: true, 5696 }, 5697 }, 5698 "see-all-photos-i-am-interested": { 5699 iaiFamily: true, 5700 VSP: { 5701 pagePath: "si:vls:see all photos:i am interested", 5702 siteSection: "search inventory", 5703 hierarchyBase: "shopping tools:i am interested", 5704 vehicleSuffix: true, 5705 }, 5706 VDP: { 5707 pagePath: "si:vehicle details:see all photos:i am interested", 5708 siteSection: "search inventory", 5709 hierarchyBase: "shopping tools:i am interested", 5710 vehicleSuffix: true, 5711 }, 5712 }, 5713 save: { 5714 VDP: { 5715 pagePath: "si:vehicle details:save", 5716 siteSection: "search inventory", 5717 hierarchyBase: "shopping tools:save", 5718 vehicleSuffix: true, 5719 }, 5720 }, 5721 // Rows 47 and 49 omit the "get quote" segment from hierarchy - implemented as written. 5722 // Both are steps of the same Crestline modal, so they replace rather than nest. 5723 "get-quote": { 5724 family: "get-quote", 5725 VDP: { 5726 pagePath: "si:vehicle details:get quote", 5727 siteSection: "search inventory", 5728 hierarchyBase: "shopping tools", 5729 vehicleSuffix: true, 5730 }, 5731 }, 5732 "get-quote-vehicle-transportation": { 5733 family: "get-quote", 5734 VDP: { 5735 pagePath: 5736 "si:vehicle details:get quote:vehicle transportation quote", 5737 siteSection: "search inventory", 5738 hierarchyBase: "shopping tools", 5739 vehicleSuffix: true, 5740 }, 5741 }, 5742 // Build & Get Quote overlays carry their own site section regardless of host page. 5743 // Row 53 omits the ":info" segment from hierarchy - implemented as written. 5744 "bg-info": { 5745 any: { 5746 pagePath: "build and get quote:info", 5747 siteSection: "build and get quote", 5748 hierarchyBase: "build and get quote", 5749 }, 5750 }, 5751 "bg-select-upfit": { 5752 any: { 5753 pagePath: "build and get quote:info:select upfit", 5754 siteSection: "build and get quote", 5755 hierarchyBase: "build and get quote:info:select upfit", 5756 }, 5757 }, 5758 "bg-request-more-info": { 5759 any: { 5760 pagePath: "build and get quote:request more info", 5761 siteSection: "build and get quote", 5762 hierarchyBase: "build and get quote:request more info", 5763 }, 5764 }, 5765 "bg-get-a-quote": { 5766 any: { 5767 pagePath: "build and get quote:get a quote for this build", 5768 siteSection: "build and get quote", 5769 hierarchyBase: "build and get quote:get a quote for this build", 5770 }, 5771 }, 5772 "ez-order-form": { 5773 family: "ez-order-form", 5774 any: { 5775 pagePath: "custom order:ez order form", 5776 siteSection: "custom order", 5777 hierarchyBase: "custom order:ez order form", 5778 vehicleSuffix: true, 5779 }, 5780 }, 5781 "ez-order-form-step-1": { 5782 family: "ez-order-form", 5783 any: { 5784 pagePath: "custom order:ez order form:step 1", 5785 siteSection: "custom order", 5786 hierarchyBase: "custom order:ez order form:step 1", 5787 vehicleSuffix: true, 5788 }, 5789 }, 5790 "ez-order-form-step-2": { 5791 family: "ez-order-form", 5792 any: { 5793 pagePath: "custom order:ez order form:step 2", 5794 siteSection: "custom order", 5795 hierarchyBase: "custom order:ez order form:step 2", 5796 vehicleSuffix: true, 5797 }, 5798 }, 5799 "ez-order-form-step-3": { 5800 family: "ez-order-form", 5801 any: { 5802 pagePath: "custom order:ez order form:step 3", 5803 siteSection: "custom order", 5804 hierarchyBase: "custom order:ez order form:step 3", 5805 vehicleSuffix: true, 5806 }, 5807 }, 5808 }; 5809 5810 // Page-load context key to overlay catalog page group. 5811 var OVERLAY_PAGE_GROUP_MAP = { 5812 VSP: "VSP", 5813 VDP: "VDP", 5814 VDPPRINT: "VDP", 5815 About: "About", 5816 CustomOrders: "CustomOrders", 5817 CommercialCatalog: "CommercialCatalog", 5818 }; 5819 5820 /** 5821 * Determines the device type based on screen width. 5822 * Uses the shared utility from modules.utilities. 5823 * @returns {string} "desktop", "tablet", or "mobile" 5824 */ 5825 function getExperienceType() { 5826 return utilities.detectDeviceType(); 5827 } 5828 5829 /** 5830 * QA helper - logs track name and optional label on one line, matching testing-doc 5831 * "Expected payload" format (track: form-submit (label: â¦)). 5832 * 5833 * @param {string} trackName - DTM track name (e.g. form-submit, click-to-call) 5834 * @param {string} [label] - Form context or descriptor for QA verification 5835 * @param {Object} digitaldata - Payload object (logged separately for DevTools expansion) 5836 */ 5837 function logTrackEventDebug(trackName, label, digitaldata) { 5838 var header = "[Ford Direct Analytics] track: " + trackName; 5839 if (label) { 5840 header += " (label: " + label + ")"; 5841 } 5842 console.log(header); 5843 console.log(digitaldata); 5844 } 5845 5846 /** 5847 * QA helper - logs a labeled mapping of our digitaldata fields to the 5848 * Adobe Analytics variables Ford expects per the WTS Page Variables spec. 5849 * Ford's Adobe Launch/DTM reads our named fields and assigns them to the 5850 * corresponding s.pageName / s.eVar* variables. Only invoked when 5851 * ?debug-fd=1 is present (enableDebug is true). 5852 * 5853 * @param {Object} digitaldata - The fully-built digitaldata object 5854 */ 5855 function logEVarSourceMap(digitaldata) { 5856 var page = (digitaldata && digitaldata.page) || {}; 5857 var user = (digitaldata && digitaldata.user) || {}; 5858 var vehicle = (digitaldata && digitaldata.vehicle) || null; 5859 var eventData = (digitaldata && digitaldata.event) || {}; 5860 var onclick = (digitaldata && digitaldata.onclick) || {}; 5861 var noVehicle = "(n/a - non-vehicle page)"; 5862 5863 console.log( 5864 "[Ford Direct Analytics] Adobe variable source map (per Ford spec):" 5865 ); 5866 console.log( 5867 "[Ford Direct Analytics] Page context and event type determine which rows below are populated (e.g. vehicle and searchDepth on vehicle-scoped VSP forms; onclick fields on click-to-call only)." 5868 ); 5869 console.table([ 5870 { Variable: "s.pageName", "Source field": "page.pageName", Value: page.pageName }, 5871 { Variable: "s.hierarchy (h1)", "Source field": "page.hierarchy", Value: page.hierarchy }, 5872 { Variable: "s.channel (Site Section)", "Source field": "page.siteSection", Value: page.siteSection }, 5873 { Variable: "s.eVar1 (P&A Code)", "Source field": "user.paCode", Value: user.paCode }, 5874 { Variable: "s.eVar2 (User Ford Region)", "Source field": "user.fordRegion", Value: user.fordRegion }, 5875 { Variable: "s.eVar3 (Dealer FDAF)", "Source field": "user.fdafCode", Value: user.fdafCode }, 5876 { Variable: "s.eVar4 (User Language)", "Source field": "page.userLanguage", Value: page.userLanguage }, 5877 { Variable: "s.eVar11 (Pagename No Vehicle)", "Source field": "page.pageNameNoVehicle", Value: page.pageNameNoVehicle },
5878 { Variable: "s.eVar12 (Model Year)", "Source field": "vehicle.modelYear", Value: vehicle ? vehicle.modelYear : noVehicle }, 5879 { Variable: "s.eVar14 (Client)", "Source field": "page.client", Value: page.client }, 5880 { Variable: "s.eVar15 (Site)", "Source field": "page.site", Value: page.site }, 5881 { Variable: "s.eVar16 (Nameplate)", "Source field": "vehicle.nameplate", Value: vehicle ? vehicle.nameplate : noVehicle }, 5882 { Variable: "s.eVar18 (Body Model Trim)", "Source field": "vehicle.bodyModelTrim", Value: vehicle ? vehicle.bodyModelTrim : noVehicle }, 5883 { Variable: "s.eVar19 (Color Code)", "Source field": "vehicle.extIntColorCode", Value: vehicle ? vehicle.extIntColorCode : noVehicle }, 5884 { Variable: "s.eVar24 (Engine/Trans)", "Source field": "vehicle.engineTrans", Value: vehicle ? vehicle.engineTrans : noVehicle }, 5885 { Variable: "s.eVar25 (Price)", "Source field": "vehicle.price", Value: vehicle ? vehicle.price : noVehicle }, 5886 { Variable: "s.eVar28 (Lead Type)", "Source field": "user.leadType", Value: user.leadType }, 5887 { Variable: "s.eVar35 (Search Depth)", "Source field": "page.searchDepth", Value: page.searchDepth }, 5888 { Variable: "s.eVar48 (Tool)", "Source field": "page.tool", Value: page.tool }, 5889 { Variable: "s.eVar49 (Tool Descriptor)", "Source field": "page.toolDescriptor", Value: page.toolDescriptor }, 5890 { Variable: "s.eVar129 (PageType)", "Source field": "page.pageType", Value: page.pageType }, 5891 { Variable: "s.eVar133 (ExperienceType)", "Source field": "page.experienceType", Value: page.experienceType }, 5892 { Variable: "event.action", "Source field": "event.action", Value: eventData.action }, 5893 { Variable: "onclick.onclick (c5)", "Source field": "onclick.onclick", Value: onclick.onclick }, 5894 { Variable: "onclick.onclickLinkName (pev2)", "Source field": "onclick.onclickLinkName", Value: onclick.onclickLinkName }, 5895 { Variable: "onclick.referredTo", "Source field": "onclick.referredTo", Value: onclick.referredTo }, 5896 ]); 5897 } 5898 5899 /** 5900 * Checks if the current page is the home page. 5901 * Home page is detected by checking if the URL path is "/" or empty. 5902 * @returns {boolean} True if on home page 5903 */ 5904 function isHomePage() { 5905 var path = window.location.pathname; 5906 return path === "/" || path === ""; 5907 } 5908 5909 /** 5910 * /definitions has no VehicleDisplayContext enum; pathname is the source of truth. 5911 * @returns {boolean} 5912 */ 5913 function isDefinitionsPage() { 5914 var path = (window.location.pathname || "").replace(/\/+$/, ""); 5915 return path.toLowerCase() === "/definitions"; 5916 } 5917 5918 /** 5919 * Many dealers serve VSP at the root URL (/). Server page context wins over pathname. 5920 * @returns {boolean} 5921 */ 5922 function isVspPage() { 5923 var pageData = getClientData("#page-context-data"); 5924 if (pageData && pageData.isVSP) { 5925 return true; 5926 } 5927 return ( 5928 fordDirectSetup && 5929 fordDirectSetup.pageContext === "SearchResults" 5930 ); 5931 } 5932 5933 /** 5934 * Gets the page context from event data or ford-direct-analytics-setup JSON. 5935 * @param {Object} event - The visor event object 5936 * @returns {string} The page context identifier (enum name string) 5937 */ 5938 function getPageContext(event) { 5939 var pageContext; 5940 5941 // Check if pageContext was passed via event data (for special pages like Home, PrivacyPolicy, VDPPRINT) 5942 if (event && event.data && event.data.pageContext) { 5943 pageContext = event.data.pageContext; 5944 if (enableDebug) { 5945 console.log( 5946 "[Ford Direct Analytics] getPageContext from event data:", 5947 pageContext 5948 ); 5949 } 5950 return pageContext; 5951 } 5952 5953 // Use our setup data which has pageContext as a string (enum name) 5954 if (fordDirectSetup && fordDirectSetup.pageContext) { 5955 pageContext = fordDirectSetup.pageContext; 5956
5957 // Special case: If pageContext is "None" and we're on the home page 5958 if (pageContext === "None" && isHomePage()) { 5959 if (enableDebug) { 5960 console.log( 5961 "[Ford Direct Analytics] getPageContext: Detected Home page (custom page at root URL)" 5962 ); 5963 } 5964 return "Home"; 5965 } 5966 5967 if (pageContext === "None" && isDefinitionsPage()) { 5968 return "Definitions"; 5969 } 5970 5971 if (enableDebug) { 5972 console.log( 5973 "[Ford Direct Analytics] getPageContext from setup:", 5974 pageContext 5975 ); 5976 } 5977 return pageContext; 5978 } 5979 5980 if (enableDebug) { 5981 console.log( 5982 "[Ford Direct Analytics] getPageContext: None (no context found)" 5983 ); 5984 } 5985 return "None"; 5986 } 5987 5988 /** 5989 * Gets vehicle data from the page if available. 5990 * @returns {Object|null} Vehicle data object or null if not available 5991 */ 5992 function getVehicleData() { 5993 var marketingData = getClientData("#marketing-events-data"); 5994 if (!marketingData) { 5995 if (enableDebug) { 5996 console.log( 5997 "[Ford Direct Analytics] getVehicleData: No marketing-events-data found" 5998 ); 5999 } 6000 return null; 6001 } 6002 6003 if (!marketingData.inventoryID && !marketingData.model && !marketingData.year) { 6004 if (enableDebug) { 6005 console.log( 6006 "[Ford Direct Analytics] getVehicleData: marketing-events-data lacks inventory context" 6007 ); 6008 } 6009 return null; 6010 } 6011 6012 var vehicleData = buildVehicleDataFromMarketingJson(marketingData, { 6013 inventoryID: marketingData.inventoryID, 6014 }); 6015 // VDP marketing-events-data keeps display-case model in nameplate until form-submit lowercases 6016 vehicleData.nameplate = marketingData.model || ""; 6017 6018 if (enableDebug) { 6019 console.log("[Ford Direct Analytics] getVehicleData:", vehicleData); 6020 } 6021 return vehicleData; 6022 } 6023 6024 /** 6025 * Gets the page context key stored during the last page load. 6026 * Set by buildDigitalData when trackPageLoad is called. 6027 * @returns {string} The page context key (e.g., "Home", "VSP", "VDP", "About") 6028 */ 6029 function getPageContextKeyFromLastLoad() { 6030 return lastPageContextKey || "unknown"; 6031 } 6032 6033 /** 6034 * @param {string} [fuelType] 6035 * @param {string} [transmissionType] 6036 * @returns {string} 6037 */ 6038 function buildEngineTrans(fuelType, transmissionType) { 6039 return [fuelType, transmissionType] 6040 .filter(function (value) { 6041 return value && String(value).trim(); 6042 }) 6043 .join("|"); 6044 } 6045 6046 /** 6047 * @param {Object} marketingData 6048 * @param {string} inventoryID 6049 * @returns {Object|null} 6050 */ 6051 function findAscItemForMarketingData(marketingData, inventoryID) { 6052 if (!window.asc_datalayer || !window.asc_datalayer.items) { 6053 return null; 6054 } 6055 6056 return ( 6057 window.asc_datalayer.items.find(function (item) { 6058 return ( 6059 (marketingData.vin && item.item_id === marketingData.vin) || 6060 String(item.item_inventory_id) === String(inventoryID) 6061 ); 6062 }) || null 6063 ); 6064 } 6065 6066 /** 6067 * @param {Object} marketingData 6068 * @param {Object} [options] 6069 * @param {string} [options.inventoryID] 6070 * @param {string} [options.bodyStyle] 6071 * @param {Object} [options.ascItem] 6072 * @returns {Object} 6073 */ 6074 function buildVehicleDataFromMarketingJson(marketingData, options) { 6075 options = options || {}; 6076 var bodyStyle = options.bodyStyle || marketingData.bodyStyle || "truck";
6077 var extIntColorCode = marketingData.color || ""; 6078 var price = 6079 marketingData.price !== undefined && 6080 marketingData.price !== null && 6081 marketingData.price !== "" 6082 ? String(marketingData.price) 6083 : ""; 6084 var engineTrans = buildEngineTrans( 6085 marketingData.fuelType, 6086 marketingData.transmissionType 6087 ); 6088 6089 var ascItem = 6090 options.ascItem || 6091 (options.inventoryID 6092 ? findAscItemForMarketingData( 6093 marketingData, 6094 options.inventoryID 6095 ) 6096 : null); 6097 if (ascItem) { 6098 if (!marketingData.bodyStyle) { 6099 if (ascItem.item_type) { 6100 bodyStyle = ascItem.item_type; 6101 } else if (ascItem.item_category) { 6102 bodyStyle = ascItem.item_category; 6103 } 6104 } 6105 if (!extIntColorCode && ascItem.item_color) { 6106 extIntColorCode = ascItem.item_color; 6107 } 6108 if ( 6109 !price && 6110 ascItem.item_price !== undefined && 6111 ascItem.item_price !== null && 6112 ascItem.item_price !== "" 6113 ) { 6114 price = String(ascItem.item_price); 6115 } 6116 if (!engineTrans && ascItem.item_fuel_type) { 6117 engineTrans = buildEngineTrans(ascItem.item_fuel_type, ""); 6118 } 6119 } 6120 6121 return { 6122 modelYear: marketingData.year || "", 6123 nameplate: (marketingData.model || "").toLowerCase(), 6124 bodyModelTrim: [ 6125 bodyStyle, 6126 marketingData.model, 6127 marketingData.trim, 6128 ] 6129 .filter(Boolean) 6130 .join("|"), 6131 extIntColorCode: extIntColorCode, 6132 engineTrans: engineTrans, 6133 price: price, 6134 }; 6135 } 6136 6137 /** 6138 * @param {string} inventoryID 6139 * @returns {Object|null} 6140 */ 6141 function getVehicleDataFromInventoryID(inventoryID) { 6142 if (!inventoryID) { 6143 return null; 6144 } 6145 6146 var marketingData = getClientData( 6147 "#analytics-marketing-data-" + inventoryID 6148 ); 6149 if (!marketingData || (!marketingData.model && !marketingData.year)) { 6150 return null; 6151 } 6152 6153 return buildVehicleDataFromMarketingJson(marketingData, { 6154 inventoryID: inventoryID, 6155 }); 6156 } 6157 6158 /** 6159 * Client-side page context when M1 page load has not yet populated lastPageContextKey. 6160 * @returns {string} 6161 */ 6162 function resolvePageContextKeyForFormSubmit() { 6163 if (isVspPage()) { 6164 return "VSP"; 6165 } 6166 6167 var pageContextKey = getPageContextKeyFromLastLoad(); 6168 if (pageContextKey && pageContextKey !== "unknown") { 6169 return pageContextKey; 6170 } 6171 6172 var pageData = getClientData("#page-context-data"); 6173 if (pageData && pageData.isVSP) { 6174 return "VSP"; 6175 } 6176 if (typeof window.isAbout !== "undefined" && window.isAbout) { 6177 return "About"; 6178 } 6179 if ( 6180 (typeof window.isEZOrder !== "undefined" && window.isEZOrder) || 6181 (typeof window.isCustomOrder !== "undefined" && window.isCustomOrder) 6182 ) { 6183 return "CustomOrders"; 6184 } 6185 if (window.inventoryID) { 6186 return "VDP"; 6187 } 6188 6189 return pageContextKey || "unknown"; 6190 } 6191 6192 /** 6193 * Baseline user/page context for form-submit when lastPageLoadData is unavailable. 6194 * @returns {Object|null} 6195 */ 6196 function getFormSubmitBaselineData() { 6197 if (lastPageLoadData) { 6198 return lastPageLoadData; 6199 } 6200 if (!_isEnabled || !fordDirectSetup) { 6201 return null; 6202 } 6203 6204 return { 6205 page: {}, 6206 user: { 6207 paCode: fordDirectSetup.paCode || "", 6208 fordRegion: fordDirectSetup.fdafRegion || "", 6209 fdafCode: fordDirectSetup.fdafCode || "", 6210 }, 6211 }; 6212 } 6213 6214 /** 6215 * @param {Object} [event] 6216 * @returns {Object|null} 6217 */ 6218 function resolveVehicleDataForFormSubmit(event) { 6219 var vehicleData = getVehicleData(); 6220 if (vehicleData) { 6221 return vehicleData; 6222 } 6223 6224 var inventoryID = 6225 event && 6226 event.data && 6227 (event.data.inventoryID || event.data.truckID); 6228 return getVehicleDataFromInventoryID(inventoryID); 6229 } 6230 6231 /** 6232 * @param {string} formContext 6233 * @returns {Object} 6234 */ 6235 function resolveFormSubmitPageGroup(formContext) { 6236 if (BUILD_AND_GET_QUOTE_FORM_CONTEXTS[formContext]) { 6237 return FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.BuildAndGetQuote; 6238 } 6239 if (formContext === "ez order form") { 6240 return FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.CustomOrders; 6241 } 6242 6243 var pageContextKey = resolvePageContextKeyForFormSubmit(); 6244 if (pageContextKey === "CustomOrders") { 6245 return FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.CustomOrders; 6246 } 6247 if (pageContextKey === "About") { 6248 return FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.About; 6249 } 6250 if (pageContextKey === "VSP") { 6251 return FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.VSP; 6252 } 6253 return FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.VDP; 6254 } 6255 6256 /** 6257 * @param {string} formContext 6258 * @param {Object} pageGroup 6259 * @param {string} vehicleCategory 6260 * @param {string} nameplate 6261 * @returns {string} 6262 */ 6263 function buildFormSubmitHierarchy( 6264 formContext, 6265 pageGroup, 6266 vehicleCategory, 6267 nameplate 6268 ) { 6269 var vehicleSuffix = 6270 vehicleCategory && nameplate 6271 ? ":" + vehicleCategory + ":" + nameplate 6272 : ""; 6273
6274 if (pageGroup === FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.BuildAndGetQuote) { 6275 return "build and get quote:" + formContext; 6276 } 6277 if (pageGroup === FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.CustomOrders) { 6278 return ( 6279 "custom order:ez order form" + 6280 (vehicleSuffix ? vehicleSuffix : "") 6281 ); 6282 } 6283 if (pageGroup === FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.About) { 6284 return "about:" + formContext + vehicleSuffix; 6285 } 6286 6287 var hierarchySegment = formContext; 6288 if (formContext === "see all photos:i am interested") { 6289 hierarchySegment = "i am interested"; 6290 } 6291 6292 return "shopping tools:" + hierarchySegment + vehicleSuffix; 6293 } 6294 6295 /** 6296 * @param {string} formContext 6297 * @param {Object} event 6298 * @returns {Object|null} 6299 */ 6300 function buildFormSubmitDigitalData(formContext, event) { 6301 var baselineData = getFormSubmitBaselineData(); 6302 if (!formContext || !baselineData) { 6303 return null; 6304 } 6305 6306 var pageGroup = resolveFormSubmitPageGroup(formContext); 6307 var vehicleData = resolveVehicleDataForFormSubmit(event); 6308 var nameplate = 6309 vehicleData && vehicleData.nameplate 6310 ? vehicleData.nameplate.toLowerCase() 6311 : ""; 6312 var vehicleCategory = vehicleData 6313 ? (vehicleData.bodyModelTrim || "").split("|")[0] || "truck" 6314 : ""; 6315 if (vehicleCategory) { 6316 vehicleCategory = vehicleCategory.toLowerCase(); 6317 } 6318 6319 var descriptor = pageGroup.leadPrefix + ":" + formContext; 6320 var pagePath = 6321 pageGroup.pageNamePrefix + 6322 ":" + 6323 formContext + 6324 ":thank you" + 6325 (nameplate ? ":" + nameplate : ""); 6326 var pageName = "dc:wts:" + pagePath; 6327 var pageNameNoVehicle = 6328 "dc:wts:" + 6329 pageGroup.pageNamePrefix + 6330 ":" + 6331 formContext + 6332 ":thank you"; 6333 6334 var experienceType = getExperienceType(); 6335 var digitaldata = { 6336 page: { 6337 pageName: pageName, 6338 siteSection: pageGroup.siteSection, 6339 hierarchy: buildFormSubmitHierarchy( 6340 formContext, 6341 pageGroup, 6342 vehicleCategory, 6343 nameplate 6344 ), 6345 userLanguage: FORD_USER_LANGUAGE, 6346 pageNameNoVehicle: pageNameNoVehicle, 6347 client: FORD_CLIENT, 6348 site: "dc:worktrucksolutions:us", 6349 radUIVersion: 6350 window.radUIVersion || "ui:rad:" + experienceType, 6351 pageDomain: window.location.hostname, 6352 pageType: pageGroup.pageType, 6353 experienceType: "ui:rad:" + experienceType, 6354 tool: "default form", 6355 toolDescriptor: descriptor, 6356 }, 6357 user: JSON.parse(JSON.stringify(baselineData.user || {})), 6358 event: { 6359 action: "lead submitted", 6360 }, 6361 }; 6362 6363 digitaldata.user.leadType = descriptor; 6364 6365 if (vehicleData && nameplate) { 6366 digitaldata.vehicle = vehicleData; 6367 } 6368 6369 if (pageGroup === FORM_SUBMIT_PREFIX_BY_PAGE_GROUP.VSP) { 6370 var searchDepth = 6371 baselineData.page && baselineData.page.searchDepth; 6372 if (!searchDepth) { 6373 var totalResults = 0; 6374 var pageData = getClientData("#page-context-data"); 6375 if (pageData && typeof pageData.totalResults !== "undefined") { 6376 totalResults = pageData.totalResults; 6377 } 6378 searchDepth = "0:" + totalResults; 6379 } 6380 digitaldata.page.searchDepth = searchDepth; 6381 } 6382 6383 return digitaldata; 6384 } 6385 6386 /** 6387 * @param {Object} event 6388 * @returns {Object} 6389 */ 6390 function buildClickToCallDigitalData(event) { 6391 var source = 6392 (event.data && event.data.clickToCallSource) || "global"; 6393 var isVehicleDetails = source === "vehicle-details"; 6394 6395 var onclickC5 = isVehicleDetails 6396 ? "wts:si:vehicle details:click to call" 6397 : "wts:help:click to call"; 6398 var onclickPev2 = isVehicleDetails 6399 ? "dc:wts:si:vehicle details:click to call" 6400 : "dc:wts:help:click to call"; 6401 var toolDescriptor = isVehicleDetails 6402 ? "click to call:vehicle details" 6403 : "click to call:global"; 6404 6405 var digitaldata = { 6406 page: lastPageLoadData 6407 ? JSON.parse(JSON.stringify(lastPageLoadData.page)) 6408 : {}, 6409 onclick: { 6410 onclick: onclickC5, 6411 contentType: "cta", 6412 contentSubType: "link",
6413 onclickLinkName: onclickPev2, 6414 }, 6415 event: { 6416 action: "click to call", 6417 }, 6418 }; 6419 6420 digitaldata.page.tool = "event:click to call"; 6421 digitaldata.page.toolDescriptor = toolDescriptor; 6422 normalizeOnclickPageFields(digitaldata); 6423 6424 var vehicleData = getVehicleData(); 6425 if (!vehicleData && event.data && event.data.inventoryID) { 6426 vehicleData = getVehicleDataFromInventoryID(event.data.inventoryID); 6427 } 6428 if (vehicleData) { 6429 digitaldata.vehicle = vehicleData; 6430 } 6431 6432 return digitaldata; 6433 } 6434 6435 /** 6436 * Builds a dynamic onclick link name for actions requiring page and modal context. 6437 * 6438 * Format varies by page context per Ford spec: 6439 * - Home (Row 66): wts:home:content:{action} (modal context ignored) 6440 * - About + modal (Row 114): wts:{modalContext}:content:{action} (no page prefix) 6441 * - Other + modal (Rows 71,76,80,86,95): wts:{pageContext}:{modalContext}:content:{action} 6442 * - Other, no modal (Row 83): wts:{pageContext}:{action} (no :content: segment) 6443 * 6444 * @param {string} actionKey - The action key to look up in DYNAMIC_ACTION_MAP 6445 * @param {string} [modalContext] - Optional modal context key (e.g., "call-popup", "i-am-interested") 6446 * @returns {string|null} The full onclick link name, or null if not a dynamic action 6447 */ 6448 function buildDynamicOnclickLinkName(actionKey, modalContext) { 6449 var action = DYNAMIC_ACTION_MAP[actionKey]; 6450 if (!action) { 6451 return null; 6452 } 6453 6454 // Get page context from lastPageLoadData 6455 var pageContextKey = getPageContextKeyFromLastLoad(); 6456 var contextPath = ONCLICK_PAGE_CONTEXT_MAP[pageContextKey] || "unknown"; 6457 6458 var onclickLinkName; 6459 var modalPath; 6460 6461 // Homepage: always wts:home:content:{action}, ignore modal context (Row 66) 6462 if (pageContextKey === "Home") { 6463 onclickLinkName = "wts:" + contextPath + ":content:" + action; 6464 } 6465 // About + modal: wts:{modalContext}:content:{action} (Row 114) 6466 else if ( 6467 modalContext && 6468 MODAL_CONTEXT_MAP[modalContext] && 6469 pageContextKey === "About" 6470 ) { 6471 modalPath = MODAL_CONTEXT_MAP[modalContext]; 6472 onclickLinkName = "wts:" + modalPath + ":content:" + action; 6473 } 6474 // Other page + modal: wts:{pageContext}:{modalContext}:content:{action} (Rows 71,76,80,86,95) 6475 else if (modalContext && MODAL_CONTEXT_MAP[modalContext]) { 6476 modalPath = MODAL_CONTEXT_MAP[modalContext]; 6477 onclickLinkName = 6478 "wts:" + contextPath + ":" + modalPath + ":content:" + action; 6479 }
6480 // No modal, non-homepage: wts:{pageContext}:{action} (Row 83, no :content:) 6481 // Direct page actions like VDP phone links, photo scroll, etc. 6482 else { 6483 onclickLinkName = "wts:" + contextPath + ":" + action; 6484 } 6485 6486 if (enableDebug) { 6487 console.log( 6488 "[Ford Direct Analytics] buildDynamicOnclickLinkName:", 6489 actionKey, 6490 "->", 6491 onclickLinkName, 6492 "(pageContext:", 6493 pageContextKey, 6494 ", modalContext:", 6495 modalContext || "none", 6496 ")" 6497 ); 6498 } 6499 6500 return onclickLinkName; 6501 } 6502 6503 /** 6504 * Returns VSP hierarchy suffix when a single chassis-model facet is active. 6505 * Format: {vehicle category}:{nameplate} 6506 * 6507 * @param {Object} [event] - Optional visor event with searchedBodyType/searchedChassisModel in data 6508 * @returns {string} Suffix without leading colon, or empty string 6509 */ 6510 function getVspHierarchySuffix(event) { 6511 var chassisModel = ""; 6512 var bodyType = ""; 6513 6514 if (event && event.data) { 6515 if (event.data.searchedChassisModel) { 6516 chassisModel = event.data.searchedChassisModel; 6517 } 6518 if (event.data.searchedBodyType) { 6519 bodyType = event.data.searchedBodyType; 6520 } 6521 } 6522 6523 if (!chassisModel) { 6524 var pageData = getClientData("#page-context-data"); 6525 if (pageData) { 6526 chassisModel = pageData.searchedChassisModel || ""; 6527 bodyType = bodyType || pageData.searchedBodyType || ""; 6528 } 6529 } 6530 6531 if (!chassisModel) { 6532 return ""; 6533 } 6534 6535 var category = (bodyType || "truck").toLowerCase(); 6536 return category + ":" + chassisModel.toLowerCase(); 6537 } 6538 6539 /** 6540 * pev2 onclickLinkName for VDP content actions (Page Variables row 83). 6541 * @returns {string} 6542 */ 6543 function buildVdpContentActionPev2() { 6544 return ( 6545 "dc:wts:si:vehicle details:content:action" + 6546 getLastLoadNameplateSuffix() 6547 ); 6548 } 6549 6550 /** 6551 * ":{nameplate}" from the last page load, or "" when no vehicle is in context. 6552 * @returns {string} 6553 */ 6554 function getLastLoadNameplateSuffix() { 6555 var nameplate = 6556 lastPageLoadData && 6557 lastPageLoadData.vehicle && 6558 lastPageLoadData.vehicle.nameplate; 6559 return nameplate ? ":" + nameplate.toLowerCase() : ""; 6560 } 6561 6562 /** 6563 * pev2 onclickLinkName for modal call flows with a distinct linkname per Ford spec. 6564 * @param {string} modalContextKey 6565 * @returns {string|null} 6566 */ 6567 function buildModalCallPev2(modalContextKey) { 6568 var pageContextKey = getPageContextKeyFromLastLoad(); 6569 var contextPath = ONCLICK_PAGE_CONTEXT_MAP[pageContextKey] || "unknown"; 6570 var modalPath = MODAL_CONTEXT_MAP[modalContextKey]; 6571 6572 if (!modalPath) { 6573 return null; 6574 } 6575 6576 // Call popup is page-context independent: Page Variables rows 21 (VSP) and 64 (About) 6577 // both specify the same linkname with no site-section segment. 6578 if (modalContextKey === "call-popup") { 6579 return "dc:wts:call popup:content:action"; 6580 } 6581 6582 return "dc:wts:" + contextPath + ":" + modalPath + ":content:action"; 6583 } 6584 6585 /** 6586 * Vehicle category segment used in Ford hierarchy strings. 6587 * @param {Object} vehicleData 6588 * @returns {string} 6589 */ 6590 function getVehicleCategory(vehicleData) { 6591 if (!vehicleData) { 6592 return ""; 6593 } 6594 return ( 6595 (vehicleData.bodyModelTrim || "").split("|")[0] || "truck" 6596 ).toLowerCase(); 6597 } 6598 6599 /** 6600 * Resolves an overlay key to its catalog definition for the current page group. 6601 * @param {string} overlayKey 6602 * @returns {Object|null} 6603 */ 6604 function resolveOverlayDefinition(overlayKey) { 6605 var entry = OVERLAY_CATALOG[overlayKey]; 6606 if (!entry) { 6607 return null; 6608 } 6609 6610 var pageGroup = 6611 OVERLAY_PAGE_GROUP_MAP[getPageContextKeyFromLastLoad()] || ""; 6612 var definition = entry.any || entry[pageGroup]; 6613 6614 if (!definition) { 6615 if (enableDebug) { 6616 console.warn( 6617 "[Ford Direct Analytics] No overlay definition for", 6618 overlayKey, 6619 "on page group", 6620 pageGroup || "(unresolved)" 6621 ); 6622 } 6623 return null; 6624 } 6625 6626 return { 6627 overlayKey: overlayKey, 6628 family: entry.family || overlayKey, 6629 iaiFamily: !!entry.iaiFamily, 6630 pagePath: definition.pagePath, 6631 siteSection: definition.siteSection, 6632 hierarchyBase: definition.hierarchyBase, 6633 vehicleSuffix: !!definition.vehicleSuffix, 6634 }; 6635 } 6636 6637 /** 6638 * Builds the digitaldata object for a global-overlay (2c) event and the stack entry 6639 * that later onclicks read from. 6640 * 6641 * @param {Object} definition Resolved overlay definition 6642 * @param {string} [inventoryID] Listing whose vehicle the overlay describes 6643 * @returns {Object} 6644 */ 6645 function buildOverlayDigitalData(definition, inventoryID) { 6646 var experienceType = getExperienceType(); 6647 var paCode = (fordDirectSetup && fordDirectSetup.paCode) || ""; 6648 var fdafCode = (fordDirectSetup && fordDirectSetup.fdafCode) || ""; 6649 var fdafRegion = (fordDirectSetup && fordDirectSetup.fdafRegion) || ""; 6650 6651 // Prefer the page-load vehicle â on VDP that is the full marketing-events-data block,
6652 // while analytics-marketing-data-{id} injected by the i-am-interested form carries only 6653 // make/model/year/trim. Only VDP sets lastPageLoadData.vehicle, so VSP falls through. 6654 var vehicleData = (lastPageLoadData && lastPageLoadData.vehicle) || null; 6655 if (!vehicleData && inventoryID) { 6656 vehicleData = getVehicleDataFromInventoryID(inventoryID); 6657 } 6658 if (!vehicleData) { 6659 vehicleData = getVehicleData(); 6660 } 6661 6662 var nameplate = 6663 vehicleData && vehicleData.nameplate 6664 ? vehicleData.nameplate.toLowerCase() 6665 : ""; 6666 6667 var pageNameNoVehicle = "dc:wts:" + definition.pagePath; 6668 var pageName = pageNameNoVehicle + (nameplate ? ":" + nameplate : ""); 6669 6670 var hierarchy = definition.hierarchyBase; 6671 if (definition.vehicleSuffix && nameplate) { 6672 hierarchy = 6673 hierarchy + 6674 ":" + 6675 getVehicleCategory(vehicleData) + 6676 ":" + 6677 nameplate; 6678 } 6679 6680 // Per DL variant 2c. No onclick block: an overlay open has no c5. 6681 // No tool/toolDescriptor: those columns are blank on every Milestone 4 overlay row. 6682 var digitaldata = { 6683 page: { 6684 pageName: pageName, 6685 siteSection: definition.siteSection, 6686 hierarchy: hierarchy, 6687 userLanguage: FORD_USER_LANGUAGE, 6688 pageNameNoVehicle: pageNameNoVehicle, 6689 client: FORD_CLIENT, 6690 site: "dc:worktrucksolutions:us", 6691 radUIVersion: window.radUIVersion || "ui:rad:" + experienceType, 6692 pageDomain: window.location.hostname, 6693 pageType: definition.siteSection, 6694 experienceType: "ui:rad:" + experienceType, 6695 }, 6696 user: { 6697 paCode: paCode, 6698 fordRegion: fdafRegion, 6699 fdafCode: fdafCode, 6700 }, 6701 }; 6702 6703 if (vehicleData) { 6704 digitaldata.vehicle = vehicleData; 6705 } 6706 6707 return digitaldata; 6708 } 6709 6710 /** 6711 * Builds the digitaldata object for a window-sticker (4c) or global-download (7c) event. 6712 * Channel and hierarchy are inherited from the last page load per both Ford rows. 6713 * 6714 * @param {Object} definition Entry from TOOL_EVENTS 6715 * @param {string} [inventoryID] 6716 * @returns {Object} 6717 */ 6718 function buildToolEventDigitalData(definition, inventoryID) { 6719 var vehicleData = 6720 (inventoryID && getVehicleDataFromInventoryID(inventoryID)) || 6721 getVehicleData() || 6722 (lastPageLoadData && lastPageLoadData.vehicle) || 6723 null; 6724 6725 var nameplate = 6726 vehicleData && vehicleData.nameplate 6727 ? vehicleData.nameplate.toLowerCase() 6728 : ""; 6729 var suffix = definition.appendNameplate && nameplate ? ":" + nameplate : ""; 6730 6731 var digitaldata = { 6732 page: lastPageLoadData 6733 ? JSON.parse(JSON.stringify(lastPageLoadData.page)) 6734 : {}, 6735 onclick: { 6736 onclick: definition.c5 + suffix, 6737 contentType: "cta", 6738 contentSubType: "link", 6739 onclickLinkName: definition.pev2 + suffix, 6740 }, 6741 user: lastPageLoadData ? lastPageLoadData.user : {}, 6742 event: { action: definition.eventAction }, 6743 }; 6744 6745 digitaldata.page.userLanguage = FORD_USER_LANGUAGE; 6746 digitaldata.page.client = FORD_CLIENT; 6747 digitaldata.page.tool = definition.tool; 6748 digitaldata.page.toolDescriptor = definition.toolDescriptor; 6749 6750 if (vehicleData) { 6751 digitaldata.vehicle = vehicleData; 6752 } 6753 6754 return digitaldata; 6755 } 6756 6757 /** 6758 * Innermost open overlay, or null. 6759 * @returns {Object|null} 6760 */ 6761 function getActiveOverlay() { 6762 return activeOverlayStack.length 6763 ? activeOverlayStack[activeOverlayStack.length - 1] 6764 : null; 6765 } 6766 6767 /** 6768 * Applies Ford global page fields on onclick payloads, then any per-action overrides. 6769 * @param {Object} digitaldata 6770 * @param {string} [actionKey] 6771 */ 6772 function normalizeOnclickPageFields(digitaldata, actionKey) { 6773 if (!digitaldata.page) { 6774 digitaldata.page = {}; 6775 } 6776 digitaldata.page.userLanguage = FORD_USER_LANGUAGE; 6777 digitaldata.page.client = FORD_CLIENT; 6778 6779 // Call Us Now copies overlay eVar11 always. Hierarchy only for I'm Interested family, 6780 // call-popup (PV 21/64), and get-quote (PV 48/50). VDP pricing phones fire this event 6781 // with no modalContext; copying every overlay would retag them from a modal that is open.
6782 var overlay = 6783 actionKey === "CALL_US_NOW_IM_INTERESTED" ? getActiveOverlay() : null; 6784 if (overlay) { 6785 digitaldata.page.pageNameNoVehicle = overlay.pageNameNoVehicle; 6786 if ( 6787 overlay.iaiFamily || 6788 overlay.overlayKey === "call-popup" || 6789 overlay.overlayKey === "get-quote" || 6790 overlay.overlayKey === "get-quote-vehicle-transportation" 6791 ) { 6792 digitaldata.page.hierarchy = overlay.hierarchy; 6793 } 6794 } 6795 6796 var overrides = actionKey && ONCLICK_PAGE_FIELD_OVERRIDES[actionKey]; 6797 if (overrides) { 6798 Object.keys(overrides).forEach(function (field) { 6799 digitaldata.page[field] = overrides[field]; 6800 }); 6801 } 6802 } 6803 6804 /** 6805 * Builds the window.digitaldata object according to Ford spec. 6806 * 6807 * pageName format: dc:wts:{pagePath} 6808 * - For VDP/VDPPRINT: dc:wts:si:vehicle details:{nameplate} 6809 * - For list views (VSP, etc.): nameplate is omitted 6810 * 6811 * @param {Object} event - The visor event object 6812 * @returns {Object} The digitaldata object 6813 */ 6814 function buildDigitalData(event) { 6815 var experienceType = getExperienceType(); 6816 6817 // Get site/user data 6818 var userData = getClientData("#user-data"); 6819 var dealerCode = fordDirectSetup ? fordDirectSetup.dealerCode : ""; 6820 var paCode = fordDirectSetup ? fordDirectSetup.paCode : ""; 6821 var fdafCode = fordDirectSetup ? fordDirectSetup.fdafCode : ""; 6822 var fdafRegion = fordDirectSetup ? fordDirectSetup.fdafRegion : ""; 6823 6824 // Determine the page context key 6825 var pageContextKey; 6826 var customPageTitle = ""; 6827 6828 // Root URL is Home or VSP â many dealers serve inventory search at / 6829 if ( 6830 isHomePage() && 6831 !(event && event.data && event.data.pageContext) 6832 ) { 6833 pageContextKey = isVspPage() ? "VSP" : "Home"; 6834 if (enableDebug) { 6835 console.log( 6836 "[Ford Direct Analytics] buildDigitalData: Root URL resolved to", 6837 pageContextKey 6838 ); 6839 } 6840 } 6841 // Check if pageContext was passed via event data (for special pages like PrivacyPolicy, VDPPRINT) 6842 else if (event && event.data && event.data.pageContext) { 6843 var eventPageContext = event.data.pageContext; 6844 pageContextKey = 6845 PAGE_CONTEXT_MAP[eventPageContext] || eventPageContext; 6846 if (enableDebug) { 6847 console.log( 6848 "[Ford Direct Analytics] buildDigitalData using event override:", 6849 pageContextKey 6850 ); 6851 } 6852 } 6853 // Use server-resolved pageContext 6854 else if (fordDirectSetup && fordDirectSetup.pageContext) { 6855 var serverContext = fordDirectSetup.pageContext; 6856 pageContextKey = PAGE_CONTEXT_MAP[serverContext] || serverContext; 6857 6858 if (isHomePage() && pageContextKey !== "VSP") { 6859 pageContextKey = "Home"; 6860 } 6861 // Check for Home page (custom page at root URL) 6862 else if (pageContextKey === "unknown" && isHomePage()) { 6863 pageContextKey = "Home"; 6864 } 6865 // Check for custom internal page 6866 else if (pageContextKey === "unknown" && window.isCustomPage) { 6867 pageContextKey = "CustomPage"; 6868 customPageTitle = (window.customPageName || "").replace( 6869 /^Custom Page:\s*/i, 6870 "" 6871 ); 6872 } 6873 6874 if (enableDebug) { 6875 console.log( 6876 "[Ford Direct Analytics] buildDigitalData using server context:", 6877 pageContextKey 6878 ); 6879 } 6880 } 6881 // Fall back to client-side detection 6882 else { 6883 var pageContext = getPageContext(event); 6884 pageContextKey = PAGE_CONTEXT_MAP[pageContext] || pageContext; 6885 if (enableDebug) { 6886 console.log( 6887 "[Ford Direct Analytics] buildDigitalData using client mapping:", 6888 pageContextKey 6889 ); 6890 } 6891 } 6892 6893 if ( 6894 (pageContextKey === "unknown" || pageContextKey === "None") && 6895 isDefinitionsPage() 6896 ) { 6897 pageContextKey = "Definitions"; 6898 } 6899 6900 // Store the resolved page context key for use by onclick events
6901 lastPageContextKey = pageContextKey; 6902 6903 // Get the page path, site section, and page type 6904 // If pageContextKey is "unknown" but server provided valid values (from URL detection), use them 6905 var pagePath, siteSection, pageType; 6906 if ( 6907 pageContextKey === "unknown" && 6908 fordDirectSetup && 6909 fordDirectSetup.pagePath && 6910 fordDirectSetup.pagePath !== "unknown" 6911 ) { 6912 // Use server-calculated values (from URL detection in _FordDirectAnalytics.cshtml) 6913 pagePath = fordDirectSetup.pagePath; 6914 siteSection = fordDirectSetup.siteSection || "unknown"; 6915 pageType = fordDirectSetup.pageType || "unknown"; 6916 if (enableDebug) { 6917 console.log( 6918 "[Ford Direct Analytics] Using server-provided page values:", 6919 pagePath 6920 ); 6921 } 6922 } else { 6923 // Use JS-side maps for known page contexts 6924 pagePath = PAGE_PATH_MAP[pageContextKey] || "unknown"; 6925 siteSection = SITE_SECTION_MAP[pageContextKey] || "unknown"; 6926 pageType = SITE_SECTION_MAP[pageContextKey] || "unknown"; 6927 } 6928 6929 // Get vehicle data for VDP/VDPPRINT pages 6930 var vehicleData = null; 6931 var nameplate = ""; 6932 if (pageContextKey === "VDP" || pageContextKey === "VDPPRINT") { 6933 vehicleData = getVehicleData(); 6934 if (vehicleData && vehicleData.nameplate) { 6935 nameplate = vehicleData.nameplate.toLowerCase(); 6936 } 6937 } 6938 6939 // Build pageName in Ford spec format: dc:wts:{pagePath} 6940 // Note: "dc" is a literal prefix per Ford spec, paCode is sent separately in user object 6941 var pageName = "dc:wts:" + pagePath; 6942 var pageNameNoVehicle = pageName; 6943 6944 // For VDP/VDPPRINT, append nameplate to pageName 6945 if ( 6946 (pageContextKey === "VDP" || pageContextKey === "VDPPRINT") && 6947 nameplate 6948 ) { 6949 pageName = pageName + ":" + nameplate; 6950 } 6951 6952 // For CustomPage, append the page title 6953 if (pageContextKey === "CustomPage" && customPageTitle) { 6954 pageName = 6955 pageName + 6956 ":" + 6957 customPageTitle.toLowerCase().replace(/\s+/g, " "); 6958 pageNameNoVehicle = "dc:wts:custom"; 6959 } 6960 6961 // Build hierarchy string per Ford spec 6962 // Format varies by page type - see spec for exact patterns 6963 var hierarchy; 6964 if (pageContextKey === "VSP") { 6965 hierarchy = "shopping tools:search inventory-vls"; 6966 var vspSuffix = getVspHierarchySuffix(event); 6967 if (vspSuffix) { 6968 hierarchy = hierarchy + ":" + vspSuffix; 6969 } 6970 } else if (pageContextKey === "VDP" || pageContextKey === "VDPPRINT") { 6971 // VDP hierarchy includes vehicle category and nameplate 6972 var vehicleCategory = vehicleData 6973 ? (vehicleData.bodyModelTrim || "").split("|")[0] || "truck" 6974 : "truck"; 6975 hierarchy = 6976 "shopping tools:search inventory-vls:" + 6977 vehicleCategory.toLowerCase() + 6978 (nameplate ? ":" + nameplate : ""); 6979 } else if (pageContextKey === "Home") { 6980 hierarchy = "home"; 6981 } else if (pageContextKey === "Sitemap") { 6982 hierarchy = "help:sitemap"; 6983 } else if (pageContextKey === "PrivacyPolicy") { 6984 hierarchy = "help:privacy"; 6985 } else if (pageContextKey === "Definitions") { 6986 hierarchy = "help:definitions"; 6987 } else if (pageContextKey === "About") { 6988 hierarchy = "about"; 6989 } else if (pageContextKey === "CustomOrders") { 6990 hierarchy = "custom order"; 6991 } else if (pageContextKey === "CommercialCatalog") { 6992 hierarchy = "commercial catalog"; 6993 } else if (pageContextKey === "MyGarage") { 6994 hierarchy = "my garage"; 6995 } else if (pageContextKey === "CustomPage") { 6996 hierarchy = 6997 "custom:" + 6998 (customPageTitle || "page").toLowerCase().replace(/\s+/g, "-"); 6999 } else if ( 7000 pageContextKey === "unknown" && 7001 fordDirectSetup && 7002 fordDirectSetup.hierarchy && 7003 fordDirectSetup.hierarchy !== "unknown" 7004 ) { 7005 // Use server-provided hierarchy for URL-detected pages (TPLogin, FAQs, etc.) 7006 hierarchy = fordDirectSetup.hierarchy; 7007 } else { 7008 hierarchy = siteSection; 7009 } 7010 7011 var digitaldata = { 7012 page: { 7013 pageName: pageName, 7014 siteSection: siteSection, 7015 hierarchy: hierarchy, 7016 userLanguage: FORD_USER_LANGUAGE, 7017 pageNameNoVehicle: pageNameNoVehicle, 7018 client: FORD_CLIENT, 7019 site: "dc:worktrucksolutions:us", 7020 radUIVersion: window.radUIVersion || "ui:rad:" + experienceType, 7021 pageDomain: window.location.hostname, 7022 pageType: pageType, 7023 experienceType: "ui:rad:" + experienceType, 7024 }, 7025 user: { 7026 paCode: paCode, 7027 fordRegion: fdafRegion, 7028 fdafCode: fdafCode, 7029 }, 7030 }; 7031 7032 // Add vehicle data and VDP lead type for VDP and VDPPRINT pages 7033 if (pageContextKey === "VDP" || pageContextKey === "VDPPRINT") { 7034 digitaldata.user.leadType = "si:vls"; 7035 if (vehicleData) { 7036 digitaldata.vehicle = vehicleData; 7037 } 7038 } 7039 7040 // Add searchDepth for VSP pages (format: "0:{totalVehicles}") 7041 // The total is passed via event data from VSP_SEARCH_COMPLETE 7042 if (pageContextKey === "VSP") { 7043 var totalResults = 0; 7044 if ( 7045 event && 7046 event.data && 7047 typeof event.data.totalResults !== "undefined" 7048 ) { 7049 totalResults = event.data.totalResults; 7050 } else { 7051 // Try to get from page data on initial load 7052 var pageData = getClientData("#page-context-data"); 7053 if (pageData && typeof pageData.totalResults !== "undefined") { 7054 totalResults = pageData.totalResults; 7055 } 7056 } 7057 digitaldata.page.searchDepth = "0:" + totalResults; 7058 } 7059 7060 return digitaldata; 7061 } 7062 7063 /** 7064 * Builds the window.digitaldata object for onclick events. 7065 * Uses lastPageLoadData for page, vehicle, and user context. 7066 * 7067 * @param {string} onclickC5 - The onclick link name (c5) for Ford spec 7068 * @param {string} onclickPev2 - The onclick link name (pev2), or null to reuse c5 7069 * @param {string} [actionKey] - Drives per-action page field overrides 7070 * @returns {Object} The digitaldata object for onclick events 7071 */ 7072 function buildOnclickDigitalData(onclickC5, onclickPev2, actionKey) { 7073 var digitaldata = { 7074 page: lastPageLoadData 7075 ? JSON.parse(JSON.stringify(lastPageLoadData.page)) 7076 : {}, 7077 onclick: { 7078 onclick: onclickC5, 7079 contentType: "cta", 7080 contentSubType: "link",
7081 onclickLinkName: onclickPev2 || onclickC5, 7082 }, 7083 vehicle: lastPageLoadData ? lastPageLoadData.vehicle : {}, 7084 user: lastPageLoadData ? lastPageLoadData.user : {}, 7085 }; 7086 normalizeOnclickPageFields(digitaldata, actionKey); 7087 return digitaldata; 7088 } 7089 7090 /** 7091 * Same-host, non-http(s), and non-navigating hrefs are not Ford 6c referral exits. 7092 * @param {string} href 7093 * @returns {boolean} 7094 */ 7095 function isSkippableReferralHref(href) { 7096 if (!href) { 7097 return true; 7098 } 7099 var trimmed = String(href).trim(); 7100 var lower = trimmed.toLowerCase(); 7101 if ( 7102 lower.indexOf("javascript:") === 0 || 7103 lower.indexOf("tel:") === 0 || 7104 lower.indexOf("mailto:") === 0 || 7105 lower === "#" 7106 ) { 7107 return true; 7108 } 7109 try { 7110 var resolved = new URL(trimmed, window.location.href); 7111 if (resolved.protocol !== "http:" && resolved.protocol !== "https:") { 7112 return true; 7113 } 7114 return resolved.hostname === window.location.hostname; 7115 } catch (err) { 7116 return true; 7117 } 7118 } 7119 7120 /** 7121 * Spec 6c c5 / referredTo. Homepage uses the literal home:homepage row; other pages 7122 * keep the same shape with the current page path so the exit is attributable. 7123 * @returns {{c5: string, referredTo: string}} 7124 */ 7125 function getReferralExitPaths() { 7126 var pageContextKey = getPageContextKeyFromLastLoad(); 7127 if (pageContextKey === "Home") { 7128 return { 7129 c5: "wts:referral:home:homepage", 7130 referredTo: "home:homepage", 7131 }; 7132 } 7133 var pagePath = PAGE_PATH_MAP[pageContextKey] || "unknown"; 7134 return { 7135 c5: "wts:referral:" + pagePath, 7136 referredTo: pagePath, 7137 }; 7138 } 7139 7140 /** 7141 * Builds digitaldata for _satellite.track("referral-exit"). Inherits last page load. 7142 * @returns {Object} 7143 */ 7144 function buildReferralExitDigitalData() { 7145 var paths = getReferralExitPaths(); 7146 var digitaldata = { 7147 page: lastPageLoadData 7148 ? JSON.parse(JSON.stringify(lastPageLoadData.page)) 7149 : {}, 7150 onclick: { 7151 onclick: paths.c5, 7152 contentType: "cta", 7153 contentSubType: "link", 7154 referredTo: paths.referredTo, 7155 onclickLinkName: "referral:exit", 7156 }, 7157 user: lastPageLoadData ? lastPageLoadData.user : {}, 7158 event: { action: "brand referral" }, 7159 }; 7160 digitaldata.page.userLanguage = FORD_USER_LANGUAGE; 7161 digitaldata.page.client = FORD_CLIENT; 7162 if (lastPageLoadData && lastPageLoadData.vehicle) { 7163 digitaldata.vehicle = lastPageLoadData.vehicle; 7164 } 7165 return digitaldata; 7166 } 7167 7168 // Third-party exits only. Body-type Helpful Resources and the Comvoy manufacturer header 7169 // are out of scope: Ford filed neither, and Helpful Resources links to WTS article pages. 7170 var REFERRAL_EXIT_SELECTOR = ["a.learn-more-link"].join(","); 7171 7172 // REQ-P01 / OQ-02: wait only for global-load. Launch tag has no id â poll is required. 7173 var SATELLITE_WAIT_MS = 5000; 7174 var SATELLITE_POLL_MS = 50; 7175 7176 function isSatelliteTrackCallable() { 7177 return typeof _satellite !== "undefined" && typeof _satellite.track === "function"; 7178 } 7179 7180 function findLaunchScript() { 7181 var scripts = document.getElementsByTagName("script"); 7182 var i; 7183 var src; 7184 for (i = 0; i < scripts.length; i++) { 7185 src = scripts[i].src || ""; 7186 if (src.indexOf("adobedtm.com") !== -1 || /launch-[^/]+\.min\.js/i.test(src)) { 7187 return scripts[i]; 7188 } 7189 } 7190 return null; 7191 } 7192 7193 /** 7194 * Calls onReady once: true when _satellite.track is callable, false when the 5s cap expires. 7195 */ 7196 function waitForSatelliteTrack(onReady) { 7197 var settled = false; 7198 var deadline = Date.now() + SATELLITE_WAIT_MS; 7199 var launchScript = findLaunchScript(); 7200 7201 function settle(ready) { 7202 if (settled) { 7203 return; 7204 } 7205 settled = true; 7206 onReady(ready); 7207 } 7208 7209 function poll() { 7210 if (settled) { 7211 return; 7212 } 7213 if (isSatelliteTrackCallable()) { 7214 settle(true); 7215 return; 7216 } 7217 if (Date.now() >= deadline) { 7218 settle(false); 7219 return; 7220 } 7221 setTimeout(poll, SATELLITE_POLL_MS); 7222 } 7223 7224 if (launchScript) { 7225 launchScript.addEventListener("load", poll); 7226 launchScript.addEventListener("error", poll); 7227 } 7228 poll(); 7229 } 7230 7231 /** 7232 * Public API - FordDirectAnalytics object 7233 */ 7234 var FordDirectAnalytics = { 7235 /** 7236 * Tracks a page load event for Ford Direct Analytics. 7237 * 7238 * ALWAYS builds the full digitaldata object from JS - this is the single source of truth. 7239 * Server-side only sets a minimal placeholder to prevent DTM timing errors. 7240 * 7241 * @param {Object} event - The visor PAGE_LOAD event object 7242 */ 7243 trackPageLoad: function (event) { 7244 // NOOP if Ford Direct Analytics is not enabled 7245 if (!_isEnabled) { 7246 if (enableDebug) { 7247 console.log( 7248 "[Ford Direct Analytics] Disabled - skipping PAGE_LOAD" 7249 ); 7250 } 7251 return; 7252 } 7253 7254 if (enableDebug) { 7255 console.log( 7256 "[Ford Direct Analytics] trackPageLoad called", 7257 event 7258 ); 7259 } 7260 7261 // Build digitaldata first to check if page is mapped 7262 var digitaldata = buildDigitalData(event); 7263 7264 // Store a deep copy for use in onclick events (must happen before early return) 7265 lastPageLoadData = JSON.parse(JSON.stringify(digitaldata)); 7266 7267 // Skip tracking for truly unmapped pages (pageContext "None" that isn't Home or CustomPage) 7268 // This keeps analytics data clean by not sending "unknown" values to Ford. 7269 // All known page types (Home, VSP, VDP, CustomPage, etc.) are explicitly mapped. 7270 // pageName format is dc:wts:{pagePath}, so check if pagePath portion is "unknown" 7271 if (digitaldata.page.pageName.indexOf(":wts:unknown") !== -1) { 7272 if (enableDebug) { 7273 console.log( 7274 "[Ford Direct Analytics] Skipping PAGE_LOAD for unmapped page type" 7275 ); 7276 } 7277 return; 7278 } 7279 7280 // Set global digitaldata for DTM 7281 window.digitaldata = digitaldata; 7282 7283 if (enableDebug) { 7284 // QA helper: show how our digitaldata fields map to the 7285 // Adobe Analytics variables Ford expects per the WTS Page 7286 // Variables spec. Ford's Adobe Launch/DTM reads these named 7287 // fields and assigns them to the corresponding s.eVar numbers. 7288 logEVarSourceMap(window.digitaldata); 7289 } 7290 7291 function fireGlobalLoad() { 7292 if (!isSatelliteTrackCallable()) { 7293 if (enableDebug) { 7294 console.warn( 7295 "[Ford Direct Analytics] _satellite not available - DTM script may not have loaded" 7296 ); 7297 } 7298 return; 7299 } 7300 // Re-bind this invocation if a later trackPageLoad overlapped the wait. 7301 window.digitaldata = digitaldata; 7302 if (enableDebug) { 7303 logTrackEventDebug("global-load", null, window.digitaldata); 7304 console.log( 7305 "[Ford Direct Analytics] Calling _satellite.track('global-load')" 7306 ); 7307 } 7308 _satellite.track("global-load"); 7309 } 7310 7311 if (isSatelliteTrackCallable()) { 7312 fireGlobalLoad(); 7313 return; 7314 } 7315 7316 waitForSatelliteTrack(function (ready) { 7317 if (!ready) { 7318 if (enableDebug) { 7319 console.warn( 7320 "[Ford Direct Analytics] _satellite not available - DTM script may not have loaded" 7321 ); 7322 } 7323 return; 7324 } 7325 fireGlobalLoad(); 7326 }); 7327 }, 7328 7329 /** 7330 * Returns whether Ford Direct Analytics is enabled. 7331 * @returns {boolean} True if enabled, false otherwise 7332 */ 7333 isEnabled: function () { 7334 return _isEnabled; 7335 }, 7336 7337 /** 7338 * Tracks a click event for Ford Direct Analytics. 7339 * 7340 * @param {Object} event - The visor event object 7341 * @param {string} eventType - The type of event (e.g., "GENERAL_LAYOUT_INTERACTION_CLICK") 7342 * @param {string} [modalContext] - Optional modal context for Call Us Now events 7343 * (e.g., "call-popup", "i-am-interested", "see-all-photos-i-am-interested") 7344 */ 7345 trackClick: function (event, eventType, modalContext) { 7346 if (!_isEnabled) { 7347 if (enableDebug) { 7348 console.log( 7349 "[Ford Direct Analytics] Disabled - skipping onclick" 7350 ); 7351 } 7352 return; 7353 } 7354 7355 var onclickC5 = null; 7356 var onclickPev2 = null; 7357 var actionKey = null; 7358
7359 // Modal context can be passed as parameter or via event.data 7360 var effectiveModalContext = 7361 modalContext || (event.data && event.data.modalContext); 7362 7363 // Determine the action key based on event type and data 7364 if (eventType === "GENERAL_LAYOUT_INTERACTION_CLICK") { 7365 // Read CTA element title from DOM data attribute 7366 // Note: browser converts data-asc-element-title to dataset.ascElementTitle 7367 actionKey = 7368 event.data.currentTarget && 7369 event.data.currentTarget.dataset.ascElementTitle; 7370 } else if (eventType === "ABOUT_EVENT") { 7371 actionKey = event.data.eventName; 7372 } else if (eventType === "INVENTORY_LAYOUT_TRACK_SEARCH_EVENT") { 7373 // VSP/VDP pages use eventName like ABOUT_EVENT 7374 actionKey = event.data.eventName; 7375 } else { 7376 actionKey = eventType; 7377 } 7378 7379 // FeaturedPhotoGallery is shared by VDP and the commercial catalog. Catalog clicks 7380 // have their own PV 77 events; suppress the VDP content-action event there. 7381 if ( 7382 getPageContextKeyFromLastLoad() === "CommercialCatalog" && 7383 VDP_CONTENT_ACTION_EVENT_KEYS[actionKey] 7384 ) { 7385 return; 7386 } 7387 7388 // Crestline quote forms use their own linknames rather than the generic modal shape 7389 var quoteModalOnclick = 7390 effectiveModalContext && 7391 QUOTE_MODAL_ONCLICKS[effectiveModalContext]; 7392 7393 // First, try to build a dynamic (page-context-aware) onclick link name 7394 onclickC5 = quoteModalOnclick 7395 ? quoteModalOnclick.c5 7396 : buildDynamicOnclickLinkName(actionKey, effectiveModalContext); 7397 7398 // If not a dynamic action, fall back to static mapping 7399 if (!onclickC5) { 7400 onclickC5 = ONCLICK_LINK_NAME_MAP[actionKey]; 7401 } 7402 7403 if (!onclickC5) { 7404 // No mapping found - this is expected for clicks that are not 7405 // Milestone 2 events (e.g., Support link opens a modal = Milestone 4). 7406 // Silently skip to avoid confusing debug output. 7407 return; 7408 } 7409 7410 if (quoteModalOnclick) { 7411 onclickPev2 = 7412 quoteModalOnclick.pev2 + getLastLoadNameplateSuffix(); 7413 } else if (effectiveModalContext) { 7414 onclickPev2 = buildModalCallPev2(effectiveModalContext); 7415 } else if (CATALOG_CONTENT_ACTION_EVENT_KEYS[actionKey]) { 7416 onclickPev2 = 7417 "dc:wts:commercial catalog:content:action" + 7418 getLastLoadNameplateSuffix(); 7419 } else if ( 7420 actionKey === "CALL_US_NOW_IM_INTERESTED" && 7421 getPageContextKeyFromLastLoad() === "Home" 7422 ) { 7423 onclickPev2 = "dc:wts:home:content:action"; 7424 } else if ( 7425 actionKey === "CALL_US_NOW_IM_INTERESTED" && 7426 getPageContextKeyFromLastLoad() === "VDP" 7427 ) { 7428 onclickPev2 = buildVdpContentActionPev2(); 7429 } else if (VDP_CONTENT_ACTION_EVENT_KEYS[actionKey]) { 7430 onclickPev2 = buildVdpContentActionPev2(); 7431 } else { 7432 onclickPev2 = ONCLICK_PEV2_MAP[actionKey] || null; 7433 } 7434 7435 var digitaldata = buildOnclickDigitalData( 7436 onclickC5, 7437 onclickPev2, 7438 actionKey 7439 ); 7440 window.digitaldata = digitaldata; 7441 7442 if (enableDebug) { 7443 logTrackEventDebug( 7444 "global-onclick", 7445 actionKey, 7446 window.digitaldata 7447 ); 7448 logEVarSourceMap(window.digitaldata); 7449 } 7450 7451 // Fire the Adobe DTM event 7452 if (typeof _satellite !== "undefined" && _satellite.track) { 7453 if (enableDebug) { 7454 console.log( 7455 "[Ford Direct Analytics] Calling _satellite.track('global-onclick')" 7456 ); 7457 } 7458 _satellite.track("global-onclick"); 7459 } else { 7460 if (enableDebug) { 7461 console.warn( 7462 "[Ford Direct Analytics] _satellite not available - DTM script may not have loaded" 7463 ); 7464 } 7465 } 7466 }, 7467 7468 /** 7469 * Tracks a global-overlay (2c) event when a Milestone 4 overlay opens, and records it 7470 * so later onclicks inside the overlay can pick up its eVar11 / hierarchy. 7471 * 7472 * @param {Object} options 7473 * @param {string} options.overlayKey - Key into OVERLAY_CATALOG 7474 * @param {string} [options.inventoryID] - Listing the overlay describes 7475 */ 7476 trackOverlay: function (options) { 7477 if (!_isEnabled) { 7478 return; 7479 } 7480 7481 var overlayKey = options && options.overlayKey; 7482 var definition = resolveOverlayDefinition(overlayKey); 7483 if (!definition) { 7484 return; 7485 } 7486
7487 // Wizard steps replace their sibling rather than nesting on top of it. 7488 for (var i = activeOverlayStack.length - 1; i >= 0; i--) { 7489 if (activeOverlayStack[i].family === definition.family) { 7490 activeOverlayStack.splice(i, 1); 7491 } 7492 } 7493 7494 var digitaldata = buildOverlayDigitalData( 7495 definition, 7496 options && options.inventoryID 7497 ); 7498 7499 activeOverlayStack.push({ 7500 overlayKey: overlayKey, 7501 family: definition.family, 7502 iaiFamily: definition.iaiFamily, 7503 pageName: digitaldata.page.pageName, 7504 hierarchy: digitaldata.page.hierarchy, 7505 pageNameNoVehicle: digitaldata.page.pageNameNoVehicle, 7506 }); 7507 7508 window.digitaldata = digitaldata; 7509 7510 if (enableDebug) { 7511 logTrackEventDebug( 7512 "global-overlay", 7513 overlayKey, 7514 window.digitaldata 7515 ); 7516 logEVarSourceMap(window.digitaldata); 7517 } 7518 7519 if (typeof _satellite !== "undefined" && _satellite.track) { 7520 _satellite.track("global-overlay"); 7521 } else if (enableDebug) { 7522 console.warn( 7523 "[Ford Direct Analytics] _satellite not available - DTM script may not have loaded" 7524 ); 7525 } 7526 }, 7527 7528 /** 7529 * Tracks a window-sticker (4c) or global-download (7c) event. 7530 * 7531 * @param {string} toolEventKey - Key into TOOL_EVENTS 7532 * @param {Object} [options] 7533 * @param {string} [options.inventoryID] 7534 */ 7535 trackToolEvent: function (toolEventKey, options) { 7536 if (!_isEnabled) { 7537 return; 7538 } 7539 7540 var definition = TOOL_EVENTS[toolEventKey]; 7541 if (!definition) { 7542 if (enableDebug) { 7543 console.warn( 7544 "[Ford Direct Analytics] Unknown tool event:", 7545 toolEventKey 7546 ); 7547 } 7548 return; 7549 } 7550 7551 window.digitaldata = buildToolEventDigitalData( 7552 definition, 7553 options && options.inventoryID 7554 ); 7555 7556 if (enableDebug) { 7557 logTrackEventDebug( 7558 definition.trackName, 7559 definition.toolDescriptor, 7560 window.digitaldata 7561 ); 7562 logEVarSourceMap(window.digitaldata); 7563 } 7564 7565 if (typeof _satellite !== "undefined" && _satellite.track) { 7566 _satellite.track(definition.trackName); 7567 } else if (enableDebug) { 7568 console.warn( 7569 "[Ford Direct Analytics] _satellite not available - DTM script may not have loaded" 7570 ); 7571 } 7572 }, 7573 7574 /** 7575 * Tracks a referral-exit (6c) event for a named buyer-facing outbound CTA. 7576 * @param {string} href 7577 */ 7578 trackReferralExit: function (href) { 7579 if (!_isEnabled) { 7580 return; 7581 } 7582 if (isSkippableReferralHref(href)) { 7583 if (enableDebug) { 7584 console.log( 7585 "[Ford Direct Analytics] Skipping referral-exit for href:", 7586 href 7587 ); 7588 } 7589 return; 7590 } 7591 7592 window.digitaldata = buildReferralExitDigitalData(); 7593 7594 if (enableDebug) { 7595 logTrackEventDebug( 7596 "referral-exit", 7597 window.digitaldata.onclick.referredTo, 7598 window.digitaldata 7599 ); 7600 logEVarSourceMap(window.digitaldata); 7601 } 7602 7603 if (typeof _satellite !== "undefined" && _satellite.track) { 7604 _satellite.track("referral-exit"); 7605 } else if (enableDebug) { 7606 console.warn( 7607 "[Ford Direct Analytics] _satellite not available - DTM script may not have loaded" 7608 ); 7609 } 7610 }, 7611 7612 /** 7613 * Removes an overlay from the stack when its modal closes. Keyed on purpose: many 7614 * Reveal modals on this site never open an overlay, and a blind pop would discard a 7615 * still-open overlay underneath them. 7616 * 7617 * @param {string} overlayKey 7618 */ 7619 closeOverlay: function (overlayKey) { 7620 for (var i = activeOverlayStack.length - 1; i >= 0; i--) { 7621 if (activeOverlayStack[i].overlayKey === overlayKey) { 7622 activeOverlayStack.splice(i, 1); 7623 if (enableDebug) { 7624 console.log( 7625 "[Ford Direct Analytics] closeOverlay:", 7626 overlayKey, 7627 "remaining:", 7628 activeOverlayStack.length 7629 ); 7630 } 7631 return; 7632 } 7633 } 7634 }, 7635 7636 /** 7637 * Tracks a form-submit (9c) event for Ford Direct Analytics. 7638 * 7639 * @param {Object} event - Visor event with formContext in event.data 7640 */ 7641 trackFormSubmit: function (event) { 7642 if (!_isEnabled) { 7643 if (enableDebug) { 7644 console.log( 7645 "[Ford Direct Analytics] Disabled - skipping form-submit" 7646 ); 7647 } 7648 return; 7649 } 7650 7651 var formContext = 7652 event && event.data && event.data.formContext; 7653 if (!formContext) { 7654 if (enableDebug) { 7655 console.log( 7656 "[Ford Direct Analytics] Skipping form-submit - no formContext" 7657 ); 7658 } 7659 return; 7660 } 7661 7662 var digitaldata = buildFormSubmitDigitalData( 7663 formContext, 7664 event 7665 ); 7666 if (!digitaldata) { 7667 if (enableDebug) { 7668 console.log( 7669 "[Ford Direct Analytics] Skipping form-submit - could not build digitaldata" 7670 ); 7671 } 7672 return; 7673 } 7674 7675 window.digitaldata = digitaldata; 7676 7677 if (enableDebug) { 7678 logTrackEventDebug("form-submit", formContext, window.digitaldata); 7679 logEVarSourceMap(window.digitaldata); 7680 } 7681 7682 if (typeof _satellite !== "undefined" && _satellite.track) { 7683 if (enableDebug) { 7684 console.log( 7685 "[Ford Direct Analytics] Calling _satellite.track('form-submit')" 7686 ); 7687 } 7688 _satellite.track("form-submit"); 7689 } else if (enableDebug) { 7690 console.warn( 7691 "[Ford Direct Analytics] _satellite not available - DTM script may not have loaded" 7692 ); 7693 } 7694 }, 7695 7696 /** 7697 * Tracks a click-to-call (5c) event for Ford Direct Analytics. 7698 * 7699 * @param {Object} event - The visor CALL_US_NOW_IM_INTERESTED event 7700 */ 7701 trackClickToCall: function (event) { 7702 if (!_isEnabled) { 7703 if (enableDebug) { 7704 console.log( 7705 "[Ford Direct Analytics] Disabled - skipping click-to-call" 7706 ); 7707 } 7708 return; 7709 } 7710 7711 // Homepage phone is a content action (1c), not click-to-call (5c) - Page Variables 7712 // row 16. That row leaves every event column blank, so the click-to-call event and 7713 // its eVar48/49 intentionally do not fire here. 7714 if (getPageContextKeyFromLastLoad() === "Home") { 7715 FordDirectAnalytics.trackClick( 7716 event, 7717 "CALL_US_NOW_IM_INTERESTED" 7718 ); 7719 return; 7720 } 7721 7722 var digitaldata = buildClickToCallDigitalData(event); 7723 window.digitaldata = digitaldata; 7724 7725 if (enableDebug) { 7726 var toolDescriptor = 7727 digitaldata && 7728 digitaldata.page && 7729 digitaldata.page.toolDescriptor; 7730 logTrackEventDebug("click-to-call", toolDescriptor, window.digitaldata); 7731 logEVarSourceMap(window.digitaldata); 7732 } 7733 7734 if (typeof _satellite !== "undefined" && _satellite.track) { 7735 if (enableDebug) { 7736 console.log( 7737 "[Ford Direct Analytics] Calling _satellite.track('click-to-call')" 7738 ); 7739 } 7740 _satellite.track("click-to-call"); 7741 } else if (enableDebug) { 7742 console.warn( 7743 "[Ford Direct Analytics] _satellite not available - DTM script may not have loaded" 7744 ); 7745 } 7746 }, 7747 }; 7748 7749 document.addEventListener("click", function (e) { 7750 var target = e.target && e.target.closest && e.target.closest(REFERRAL_EXIT_SELECTOR); 7751 if (!target) { 7752 return; 7753 } 7754 FordDirectAnalytics.trackReferralExit(target.href); 7755 });
7756 7757 // Export to modules 7758 modules.FordDirectAnalytics = FordDirectAnalytics; 7759})(window.wts, window.wts.modules); 7760; 7761(function (wts, modules) { 7762 /** 7763 * The location of our visor event listeners 7764 * Marketing libraries should hook into visor from here 7765 * 7766 * @example 7767 * wts.visor.on(UI_EVENTS.PAGE_VIEW, function(e){ 7768 * //... 7769 * }); 7770 * 7771 */ 7772 7773 var UI_EVENTS = wts.visor.Events; 7774 var AscTracking = modules.AscTracking; 7775 var AdobeAnalytics = modules.AdobeAnalytics; 7776 var FordDirectAnalytics = modules.FordDirectAnalytics; 7777 var getClientData = modules.getClientData; 7778 7779 // this script is rendered before the @data section, so we must defer 7780 var pageData = getClientData("#page-context-data"); 7781 7782 // TODO: this is used by setAnalyticsCTAEvent 7783 //var analyticsData = getClientData("#ga-data"); 7784 var vehicleCategory = pageData.isVast ? "VAST" : "Vehicle"; 7785 7786 const trackAdobeEvent = window.TrackAdobeEvent || console.log; 7787 7788 wts.visor.on(UI_EVENTS.PAGE_VIEW, function (event) { 7789 var MarketingEvents = modules.MarketingEvents; 7790 var VehicleViewData = modules.VehicleViewData; 7791 var data = getClientData("#marketing-events-data"); 7792 7793 if (event.data.context === "VDP") { 7794 7795 if (data.stockType !== "Used") { 7796 data.stockType = "New"; 7797 } 7798 7799 var vehicleViewData = new VehicleViewData( 7800 data.inventoryID, 7801 data.dealerInventoryNumericID, 7802 data.zipCode, 7803 data.make, 7804 data.model, 7805 data.year, 7806 data.stockType, 7807 data.color, 7808 data.transmissionType, 7809 data.bodyStyle, 7810 data.fuelType, 7811 data.driveTrain, 7812 data.price, 7813 data.trim, 7814 data.mileage, 7815 data.vin, 7816 data.fuelType 7817 ); 7818 7819 MarketingEvents.vehicleView(vehicleViewData); 7820 AscTracking.trackPageViewEvent(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_ITEM_PAGEVIEW)); 7821 } else if (event.data.context === "VAST" || event.data.context === "VSP") { 7822 AscTracking.trackPageViewEvent(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_ITEMLIST_PAGEVIEW)); 7823 } 7824 7825 const ascEventData = { 7826 pageType: event.data.context 7827 }; 7828 7829 AscTracking.trackPageViewEvent(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_PAGEVIEW, { detail: ascEventData })); 7830 }); 7831 7832 wts.visor.on(UI_EVENTS.PAGE_LOAD, function (event) { 7833 // Check if Ford Direct Analytics is enabled - if so, use it exclusively (mutual exclusivity) 7834 var fordDirectSetup = getClientData("#ford-direct-analytics-setup"); 7835 var isFordDirectEnabled = fordDirectSetup && fordDirectSetup.isEnabled; 7836 7837 if (isFordDirectEnabled) { 7838 // Ford Direct Analytics - exclusive path when enabled 7839 if (FordDirectAnalytics) { 7840 FordDirectAnalytics.trackPageLoad(event); 7841 } 7842 } else { 7843 // Existing Adobe Analytics (Stellantis) - only runs when Ford Direct is disabled 7844 var adobePageData = AdobeAnalytics.getAdobePageLoadEventData(event); 7845 if (adobePageData) { 7846 trackAdobeEvent(new CustomEvent("page-load", { detail: adobePageData })); 7847 } 7848 } 7849 }); 7850 7851 wts.visor.on(UI_EVENTS.INTERACTION_CLICK, function (event) { 7852 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(event); 7853 if (adobePageData) { 7854 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 7855 } 7856 7857 const ascEventData = { 7858 elementText: event.data.currentTarget.innerText.trim(), 7859 elementTitle: event.data.currentTarget.dataset.ascElementTitle || "", 7860 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.ACCESSABILITY_TOOL, 7861 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.CTA_BUTTON, 7862 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 7863 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.SEARCH 7864 }; 7865 AscTracking.trackInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: ascEventData })); 7866 });
7867 7868 wts.visor.on(UI_EVENTS.SEE_MORE_INTERACTION_CLICK, function (event) { 7869 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(event); 7870 if (adobePageData) { 7871 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 7872 } 7873 }); 7874 7875 wts.visor.on(UI_EVENTS.GENERAL_LAYOUT_INTERACTION_CLICK, function (event) { 7876 var adobeEventData = AdobeAnalytics.getAdobeEventDataFromGeneralLayoutTarget(event.data.currentTarget); 7877 7878 if (adobeEventData) { 7879 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(adobeEventData); 7880 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 7881 } 7882 7883 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: event.data.currentTarget })); 7884 7885 // Ford Direct Analytics - parallel tracking path 7886 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 7887 FordDirectAnalytics.trackClick(event, "GENERAL_LAYOUT_INTERACTION_CLICK"); 7888 } 7889 }); 7890 7891 wts.visor.on(UI_EVENTS.VOCATIONAL_CAROUSEL_PAGE_CHANGE, function (event) { 7892 var adobeEventData = AdobeAnalytics.getAdobeEventDataFromVocationalPageChange(event.data.pageChangeEventData); 7893 7894 if (adobeEventData) { 7895 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(adobeEventData); 7896 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 7897 } 7898 7899 AscTracking.trackVocationalCarouselPageChange(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: event.data.pageChangeEventData })); 7900 }); 7901 7902 wts.visor.on(UI_EVENTS.PAGINATION_CONTROL_CHANGE, function (event) { 7903 var adobeEventData = AdobeAnalytics.getAdobeEventDataFromPaginationControls(event.data.currentTarget); 7904 7905 if (adobeEventData) { 7906 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(adobeEventData); 7907 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 7908 } 7909 7910 AscTracking.trackPaginationControlChange(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_ELEMENT_CONFIGURATION, { detail: event.data.currentTarget })); 7911 }); 7912 7913 wts.visor.on(UI_EVENTS.VSP_SEARCH_COMPLETE, function (e) { 7914 var viewModel = e.data.viewModel; 7915 7916 // Ford Direct Analytics - fire PAGE_LOAD for search results update (same pattern as PAGE_LOAD event) 7917 var fordDirectSetup = getClientData("#ford-direct-analytics-setup"); 7918 var isFordDirectEnabled = fordDirectSetup && fordDirectSetup.isEnabled; 7919 7920 if (isFordDirectEnabled && FordDirectAnalytics && pageData && pageData.isVSP) { 7921 var pathname = window.location.pathname; 7922 var isRootHome = pathname === "/" || pathname === ""; 7923 7924 if (!isRootHome) { 7925 // Pass totalResults for searchDepth field per Ford spec 7926 // TotalResults is nested under viewModel.State (see truck-search.js) 7927 var totalResults = 7928 viewModel && 7929 viewModel.State && 7930 viewModel.State.TotalResults 7931 ? viewModel.State.TotalResults 7932 : 0; 7933 FordDirectAnalytics.trackPageLoad({ 7934 data: { 7935 pageContext: "SearchResults", 7936 totalResults: totalResults, 7937 searchedChassisModel: 7938 (viewModel && viewModel.ResultsSearchedChassisModel) || 7939 "", 7940 searchedBodyType: 7941 (viewModel && viewModel.ResultsSearchedBodyType) || "", 7942 }, 7943 }); 7944 } 7945 } 7946 7947 // Fire both the itemlist and the generic pageview events, mirroring the initial page load behavior 7948 const ascEventData = { 7949 pageType: viewModel.Layout.FeatureArea 7950 }; 7951 7952 AscTracking.trackPageViewEvent(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_ITEMLIST_PAGEVIEW, { detail: ascEventData })); 7953 AscTracking.trackPageViewEvent(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_PAGEVIEW, { detail: ascEventData })); 7954 });
7955 7956 wts.visor.on(UI_EVENTS.FORM_START, function (event) { 7957 var adobeEventData = AdobeAnalytics.getAdobeEventDataFromFormStart(event.data.currentTarget); 7958 7959 if (adobeEventData) { 7960 var adobePageData = AdobeAnalytics.getAdobeFormStartClickEventData(adobeEventData); 7961 trackAdobeEvent(new CustomEvent("form-start", { detail: adobePageData })); 7962 } 7963 7964 AscTracking.initializeFormStart(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_ENGAGEMENT, { detail: { currentTarget: event.data.currentTarget } })); 7965 }); 7966 7967 wts.visor.on(UI_EVENTS.FORM_ENGAGE, function (event) { 7968 AscTracking.trackFormEngage(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_ENGAGEMENT, { detail: { currentTarget: event.data.currentTarget } })); 7969 }); 7970 7971 wts.visor.on(UI_EVENTS.CALL_US_NOW_IM_INTERESTED, function (event) { 7972 7973 if (!wts.user.isTruckProOrAbove) { 7974 7975 // CASE: on different pages this has different categories 7976 var category = "Vehicle"; 7977 var inventoryID = event.data.inventoryID; 7978 var gaPhoneLabel = 7979 event.data && 7980 event.data.gaPhoneLabel && 7981 String(event.data.gaPhoneLabel).trim(); 7982 var commPhoneOverride = 7983 event.data && 7984 event.data.commPhoneNumber && 7985 String(event.data.commPhoneNumber).trim(); 7986 7987 if (typeof window.analyticsDimensions !== "undefined") { 7988 category = "Search"; 7989 } 7990 7991 getLabelAndDimensions(inventoryID, function (label, dimensions) { 7992 var eventLabel = gaPhoneLabel || commPhoneOverride || label; 7993 window.TrackEvent(category, "Phone Call", eventLabel, dimensions); 7994 }); 7995 } 7996 7997 // Ford Direct Analytics - parallel tracking path 7998 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 7999 var modalContext = event.data && event.data.modalContext; 8000 if (modalContext) { 8001 FordDirectAnalytics.trackClick( 8002 event, 8003 "CALL_US_NOW_IM_INTERESTED", 8004 modalContext 8005 ); 8006 } else { 8007 FordDirectAnalytics.trackClickToCall(event); 8008 } 8009 } 8010 }); 8011 8012 wts.visor.on(UI_EVENTS.CALLBACK_REQUEST_CLICK, function (event) { 8013 AscTracking.initializeFormStart(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_ENGAGEMENT, { 8014 detail: { 8015 currentTarget: event.data.currentTarget 8016 } 8017 })); 8018 8019 // Fire the start engagement event 8020 AscTracking.trackFormEngage(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_ENGAGEMENT, { 8021 detail: { 8022 currentTarget: event.data.currentTarget, 8023 formName: 'Callback Form', 8024 eventName: 'Request a Callback', 8025 commStatus: 'start' 8026 } 8027 })); 8028 }); 8029 8030 wts.visor.on(UI_EVENTS.CLICK_SUPPORT_LINK, function () { 8031 TrackEvent("Support", "Support Link Clicked", ""); 8032 window.lastEvent = "Support Link Clicked"; 8033 }); 8034 8035 // Ford Direct Analytics - support form submission tracking 8036 wts.visor.on(UI_EVENTS.SUBMIT_SUPPORT_FORM, function (event) { 8037 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8038 FordDirectAnalytics.trackClick(event, "SUBMIT_SUPPORT_FORM"); 8039 } 8040 }); 8041 8042 // Ford Direct Analytics - Milestone 4 global-overlay open/close 8043 wts.visor.on(UI_EVENTS.FORD_OVERLAY_OPEN, function (event) { 8044 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8045 FordDirectAnalytics.trackOverlay(event.data); 8046 } 8047 }); 8048 8049 wts.visor.on(UI_EVENTS.FORD_OVERLAY_CLOSE, function (event) { 8050 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8051 FordDirectAnalytics.closeOverlay(event.data && event.data.overlayKey); 8052 } 8053 }); 8054 8055 wts.visor.on(UI_EVENTS.SUBMIT_VEHICLE_INQUIRY_FORM, function (e) { 8056 var inventoryID = e.data.InventoryID; 8057 var pathType = e.data.PathType; 8058 var messageType = e.data.MessageType; 8059 8060 modules.MarketingEvents.vehicleLead(new modules.VehicleLeadData(inventoryID)); 8061 8062 var formName = { form_name: messageType }; 8063 8064 if (inventoryID) { 8065 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8066 dimensions = Object.assign({ 8067 'path_type': pathType, 8068 'form_name': messageType
8069 }, dimensions); 8070 8071 window.TrackEvent(vehicleCategory, "New Lead", label, dimensions); 8072 }); 8073 } 8074 else if (window.isGarage) { 8075 window.TrackEvent("Garage", "New Lead", null, formName); 8076 } 8077 else if (window.isAbout) { 8078 window.TrackEvent("About", "New Lead", null, formName); 8079 } 8080 else if (window.isCustomPage) { 8081 window.TrackEvent(window.customPageName, "New Lead", null, formName); 8082 } 8083 else if (pageData.isVSP) { 8084 // a VSP without an inventoryID must be No Results... right? 8085 window.TrackEvent("Search", "New Lead", null, formName); 8086 } 8087 8088 return modules.MarketingEvents.wait(); 8089 }); 8090 8091 wts.visor.on(UI_EVENTS.SUBMIT_VEHICLE_INQUIRY_FORM_ERROR, function (event) { 8092 trackAdobeEvent(new CustomEvent("error-display", { 8093 detail: { 8094 error: { 8095 message: event.data.message, 8096 type: event.data.type 8097 } 8098 } 8099 })); 8100 }); 8101 8102 wts.visor.on(UI_EVENTS.SUBMIT_LOAD_GARAGE_FORM, function () { 8103 TrackEvent("Sign Up", "New Lead"); 8104 }); 8105 8106 wts.visor.on(UI_EVENTS.WAIT_FOR_FORM_CONFIRMATION, function (e) { 8107 var data = e.data; 8108 var inventoryID = data.inventoryID; 8109 var messageType = data.messageType; 8110 var dealerMessageID = data.dealerMessageID; 8111 8112 var adobePageData = AdobeAnalytics.getAdobeFormSubmitData(e); 8113 if (adobePageData) { 8114 trackAdobeEvent(new CustomEvent("form-submit", { detail: adobePageData })); 8115 } 8116 8117 AscTracking.trackFormSubmission(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_SUBMISSION, { detail: { uniqueID: data.uniqueID, inventoryID: data.inventoryID, messageType: messageType } })); 8118 8119 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8120 if (!data.isSupportRequest && !data.isNoResultsMessage) { 8121 FordDirectAnalytics.trackFormSubmit(e); 8122 } 8123 } 8124 8125 if (data.isSupportRequest) { 8126 TrackEvent("Support", "Support Link Clicked Confirmed"); 8127 return; 8128 } 8129 8130 var action = (function () { 8131 8132 if (data.isCallbackMessage) { 8133 return "Phone Dealer Confirmed"; 8134 } 8135 if (data.isNoResultsMessage) { 8136 return "No Results Confirmed"; 8137 } 8138 8139 if (window.lastEvent === "Tell Me More" || 8140 window.lastEvent === "I'm Interested" || 8141 window.lastEvent === "Email Dealer" || 8142 window.lastEvent === "Bare Chassis Request") { 8143 return window.lastEvent + " Confirmed"; 8144 } 8145 8146 return ""; 8147 })(); 8148 8149 var formName = { form_name: messageType }; 8150 8151 if (window.lastEvent === "Support Link Clicked" || window.lastEvent === "Support Link Clicked Confirmed") { 8152 TrackEvent("Support", action, "", formName); 8153 } 8154 else if (inventoryID) { 8155 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8156 dimensions = Object.assign({ 8157 'form_name': messageType, 8158 'dealer_message_id': dealerMessageID, // TODO: need to decide the dimension name 8159 }, dimensions); 8160 window.TrackEvent(vehicleCategory, action, label, dimensions); 8161 }); 8162 } 8163 else if (typeof window.isGarage !== "undefined") { 8164 // trackGarageEvent(action); 8165 TrackEvent("Garage", action, null, formName); 8166 } 8167 else if (typeof window.isAbout !== "undefined") { 8168 // trackAboutEvent(action); 8169 TrackEvent("About", action, null, formName); 8170 } 8171 else if (typeof window.isEZOrder !== "undefined") { 8172 // trackEZOrderEvent(action) 8173 TrackEvent("EZOrder", action, null, formName); 8174 } 8175 else if (typeof window.isCustomOrder !== "undefined") { 8176 // trackCustomOrderEvent(action) 8177 TrackEvent("CustomOrder", action, null, formName); 8178 } 8179 else if (window.isCustomPage) { 8180 TrackEvent(window.customPageName, action, null, formName); 8181 } 8182 else if (typeof trackSearchEvent === "function") { 8183 // there are 2 trackSearchEvents â one for searchv1 and one for v2 (in _InventoryLayout) 8184 // trackSearchEvent(action); 8185 TrackEvent("Search", action, null, formName); 8186 } 8187 8188 });
8189 8190 wts.visor.on(UI_EVENTS.OPEN_VEHICLE_INQUIRY_FORM, function (e) { 8191 // TIPS FOR WIZARDS: 8192 // When interacting with the CTA on VSP we trigger "Tell Me More" 8193 // and on VDP we trigger "I'm Interested" 8194 // 8195 var inventoryID = e.data.inventoryID; 8196 8197 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8198 if (pageData.isVSP) { 8199 window.TrackEvent(vehicleCategory, "Tell Me More", label, dimensions); 8200 } 8201 else if (pageData.isVDP) { 8202 window.TrackEvent(vehicleCategory, "I'm Interested", label, dimensions); 8203 } 8204 }); 8205 }); 8206 8207 wts.visor.on(UI_EVENTS.VEHICLE_DETAILS_INTERNAL_LINK_CLICK, function (e) { 8208 var inventoryID = e.data.inventoryID; 8209 var attributeName = e.data.attributeName; 8210 8211 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8212 window.TrackEvent(vehicleCategory, "VDP Internal Link Click", `${attributeName} - ${label}`, dimensions); 8213 }); 8214 }); 8215 8216 wts.visor.on(UI_EVENTS.CLICK_FEATURED_PHOTO_THUMB, function (e) { 8217 TrackEvent(vehicleCategory, "Click Thumbnail [C]", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8218 8219 const ascEventData = { 8220 elementText: e.data.description, 8221 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8222 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8223 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.OPEN, 8224 inventoryID: e.data.inventoryID, 8225 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.IMAGE, 8226 linkUrl: null, 8227 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8228 }; 8229 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8230 8231 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8232 FordDirectAnalytics.trackClick(e, "PHOTO_GALLERY_CLICK_THUMBNAIL"); 8233 } 8234 }); 8235 8236 wts.visor.on(UI_EVENTS.SUBMIT_CUSTOM_FORM, function (e) { 8237 var action = "Submit Form"; 8238 var category = "Custom Form"; 8239 var label = `${e.data.dealerName} - ${e.data.formName}`; 8240 TrackEvent(category, action, label, { dealer_message_id: e.data.dealerMessageID }); 8241 8242 AscTracking.trackFormSubmission(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_SUBMISSION, { 8243 detail: { 8244 uniqueID: e.data.dealerMessageID, 8245 messageType: e.data.formName, 8246 formElement: e.data.formElement 8247 } 8248 })); 8249 }); 8250 8251 wts.visor.on(UI_EVENTS.PHOTO_GALLERY_OPEN, function (e) { 8252 var index = e.data.current; 8253 var isVDP = e.data.isVDP; 8254 var inventoryID = e.data.inventoryID; 8255 8256 var isBodySide = index === 1; 8257 8258 var side = isVDP ? "[D]" : "[C]"; 8259 if (isBodySide) { 8260 side = isVDP ? "[DB]" : "[B]"; 8261 } 8262 8263 var action = ["View Photos ", side].join(""); 8264 8265 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8266 window.TrackEvent(vehicleCategory, action, label, dimensions); 8267 }); 8268 8269 const ascEventData = { 8270 elementText: e.data.description, 8271 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8272 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8273 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.OPEN, 8274 inventoryID: e.data.inventoryID, 8275 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.IMAGE, 8276 linkUrl: null, 8277 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8278 }; 8279 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8280 }); 8281 8282 wts.visor.on(UI_EVENTS.PHOTO_GALLERY_CLOSE, function (e) { 8283 8284 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8285 if (adobePageData) { 8286 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8287 } 8288 8289 const ascEventData = { 8290 elementText: e.data.description, 8291 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8292 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8293 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.CLOSE, 8294 inventoryID: e.data.inventoryID, 8295 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8296 linkUrl: null, 8297 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8298 }; 8299 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8300 });
8301 8302 wts.visor.on(UI_EVENTS.PHOTO_GALLERY_NEXT_PHOTO, function (e) { 8303 var inventoryID = e.data.inventoryID; 8304 8305 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8306 window.TrackEvent(vehicleCategory, "View Next Photo", label, dimensions); 8307 }); 8308 8309 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8310 if (adobePageData) { 8311 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8312 } 8313 8314 const ascEventData = { 8315 elementText: e.data.description, 8316 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8317 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8318 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.INCREASE, 8319 inventoryID: e.data.inventoryID, 8320 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8321 linkUrl: null, 8322 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8323 }; 8324 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8325 8326 // Ford Direct Analytics - parallel tracking path 8327 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8328 FordDirectAnalytics.trackClick(e, "PHOTO_GALLERY_NEXT_PHOTO"); 8329 } 8330 }); 8331 8332 wts.visor.on(UI_EVENTS.PHOTO_GALLERY_PREVIOUS_PHOTO, function (e) { 8333 var inventoryID = e.data.inventoryID; 8334 8335 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8336 window.TrackEvent(vehicleCategory, "View Previous Photo", label, dimensions); 8337 }); 8338 8339 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8340 if (adobePageData) { 8341 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8342 } 8343 8344 const ascEventData = { 8345 elementText: e.data.description, 8346 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8347 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8348 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.DECREASE, 8349 inventoryID: e.data.inventoryID, 8350 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8351 linkUrl: null, 8352 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8353 }; 8354 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8355 8356 // Ford Direct Analytics - parallel tracking path 8357 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8358 FordDirectAnalytics.trackClick(e, "PHOTO_GALLERY_PREVIOUS_PHOTO"); 8359 } 8360 }); 8361 8362 wts.visor.on(UI_EVENTS.PHOTO_GALLERY_CLICK_THUMBNAIL, function (e) { 8363 var inventoryID = e.data.inventoryID; 8364 8365 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8366 window.TrackEvent(vehicleCategory, "View Photo via Thumbnail", label, dimensions); 8367 }); 8368 8369 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8370 if (adobePageData) { 8371 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8372 } 8373 8374 const ascEventData = { 8375 elementText: e.data.description, 8376 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8377 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8378 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.OPEN, 8379 inventoryID: e.data.inventoryID, 8380 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.IMAGE, 8381 linkUrl: null, 8382 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8383 }; 8384 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8385 8386 // Ford Direct Analytics - parallel tracking path 8387 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8388 FordDirectAnalytics.trackClick(e, "PHOTO_GALLERY_CLICK_THUMBNAIL"); 8389 } 8390 });
8391 8392 wts.visor.on(UI_EVENTS.PHOTO_GALLERY_NEXT_PAGE, function (e) { 8393 var inventoryID = e.data.inventoryID; 8394 8395 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8396 window.TrackEvent(vehicleCategory, "Page Thumbnails Next", label, dimensions); 8397 }); 8398 8399 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8400 if (adobePageData) { 8401 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8402 } 8403 8404 const ascEventData = { 8405 elementText: e.data.description, 8406 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8407 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8408 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.INCREASE, 8409 inventoryID: e.data.inventoryID, 8410 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8411 linkUrl: null, 8412 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8413 }; 8414 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8415 }); 8416 8417 wts.visor.on(UI_EVENTS.PHOTO_GALLERY_PREVIOUS_PAGE, function (e) { 8418 var inventoryID = e.data.inventoryID; 8419 8420 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8421 window.TrackEvent(vehicleCategory, "Page Thumbnails Previous", label, dimensions); 8422 }); 8423 8424 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8425 if (adobePageData) { 8426 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8427 } 8428 8429 const ascEventData = { 8430 elementText: e.data.description, 8431 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8432 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8433 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.DECREASE, 8434 inventoryID: e.data.inventoryID, 8435 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8436 linkUrl: null, 8437 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8438 }; 8439 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8440 }); 8441 8442 wts.visor.on(UI_EVENTS.FEATURED_PHOTO_GALLERY_NEXT_PHOTO, function (e) { 8443 var inventoryID = e.data.inventoryID; 8444 8445 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8446 window.TrackEvent(vehicleCategory, "View Next Photo", label, dimensions); 8447 }); 8448 8449 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8450 if (adobePageData) { 8451 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8452 } 8453 8454 const ascEventData = { 8455 elementText: e.data.description, 8456 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8457 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8458 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.INCREASE, 8459 inventoryID: e.data.inventoryID, 8460 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8461 linkUrl: null, 8462 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8463 }; 8464 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8465 8466 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8467 FordDirectAnalytics.trackClick(e, "PHOTO_GALLERY_NEXT_PHOTO"); 8468 } 8469 }); 8470 8471 wts.visor.on(UI_EVENTS.FEATURED_PHOTO_GALLERY_PREVIOUS_PHOTO, function (e) { 8472 var inventoryID = e.data.inventoryID; 8473 8474 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8475 window.TrackEvent(vehicleCategory, "View Previous Photo", label, dimensions); 8476 }); 8477 8478 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8479 if (adobePageData) { 8480 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8481 } 8482 8483 const ascEventData = { 8484 elementText: e.data.description, 8485 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8486 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8487 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.DECREASE, 8488 inventoryID: e.data.inventoryID, 8489 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8490 linkUrl: null, 8491 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8492 }; 8493 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8494 8495 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8496 FordDirectAnalytics.trackClick(e, "PHOTO_GALLERY_PREVIOUS_PHOTO"); 8497 } 8498 });
8499 8500 wts.visor.on(UI_EVENTS.FEATURED_PHOTO_GALLERY_VIEW_PHOTO, function (e) { 8501 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8502 FordDirectAnalytics.trackClick(e, "FEATURED_PHOTO_GALLERY_VIEW_PHOTO"); 8503 } 8504 }); 8505 8506 wts.visor.on(UI_EVENTS.FEATURED_PHOTO_GALLERY_NEXT_PAGE, function (e) { 8507 var inventoryID = e.data.inventoryID; 8508 8509 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8510 window.TrackEvent(vehicleCategory, "Page Thumbnails Next", label, dimensions); 8511 }); 8512 8513 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8514 if (adobePageData) { 8515 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8516 } 8517 8518 const ascEventData = { 8519 elementText: e.data.description, 8520 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8521 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8522 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.INCREASE, 8523 inventoryID: e.data.inventoryID, 8524 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8525 linkUrl: null, 8526 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8527 }; 8528 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8529 }); 8530 8531 wts.visor.on(UI_EVENTS.FEATURED_PHOTO_GALLERY_PREVIOUS_PAGE, function (e) { 8532 var inventoryID = e.data.inventoryID; 8533 8534 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8535 window.TrackEvent(vehicleCategory, "Page Thumbnails Previous", label, dimensions); 8536 }); 8537 8538 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8539 if (adobePageData) { 8540 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8541 } 8542 8543 const ascEventData = { 8544 elementText: e.data.description, 8545 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8546 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8547 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.DECREASE, 8548 inventoryID: e.data.inventoryID, 8549 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8550 linkUrl: null, 8551 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8552 }; 8553 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8554 }); 8555 8556 wts.visor.on(UI_EVENTS.PHOTO_GALLERY_VIEW_PHOTO_NEW_TAB, function (e) { 8557 getLabelAndDimensions(e.data.inventoryID, function (label, dimensions) { 8558 window.TrackEvent(vehicleCategory, "Open in New Tab", label, dimensions); 8559 }); 8560 8561 const ascEventData = { 8562 elementText: e.data.description, 8563 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.GALLERY, 8564 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8565 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.OPEN, 8566 inventoryID: e.data.inventoryID, 8567 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.HYPERLINK, 8568 linkUrl: null, 8569 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.GALLERY, 8570 }; 8571 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8572 }); 8573 8574 wts.visor.on(UI_EVENTS.CLICK_CONDITION_LINK, function (e) { 8575 TrackEvent(vehicleCategory, "View Condition", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8576 }); 8577 8578 wts.visor.on(UI_EVENTS.CLICK_OUTBOUND_LINK, function (e) { 8579 var linkURL = e.data; 8580 TrackEvent(vehicleCategory, "View Outbound Link", linkURL, window.myAnalyticsDimensions); 8581 8582 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8583 FordDirectAnalytics.trackReferralExit(linkURL); 8584 } 8585 });
8586 8587 wts.visor.on(UI_EVENTS.CLICK_WINDOWSTICKER_LINK, function (e) { 8588 TrackEvent(vehicleCategory, "Window Sticker", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8589 8590 // Ford Direct Analytics - parallel tracking path 8591 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8592 FordDirectAnalytics.trackToolEvent("window-sticker"); 8593 } 8594 }); 8595 8596 wts.visor.on(UI_EVENTS.CLICK_BODY_DATASHEET_LINK, function (e) { 8597 var category = pageData.isVast ? "VAST" : "Branding"; 8598 TrackEvent(category, "Click Datasheet", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8599 8600 // Ford Direct Analytics - parallel tracking path 8601 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8602 FordDirectAnalytics.trackToolEvent("download-vehicle-details"); 8603 } 8604 }); 8605 8606 wts.visor.on(UI_EVENTS.CLICK_CHASSIS_DATASHEET_LINK, function (e) { 8607 var category = pageData.isVast ? "VAST" : "Branding"; 8608 var resourceURL = e.data; 8609 TrackEvent(category, "Click Datasheet", resourceURL, window.myAnalyticsDimensions); 8610 8611 // Ford Direct Analytics - parallel tracking path 8612 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8613 FordDirectAnalytics.trackToolEvent("download-vehicle-details"); 8614 } 8615 }); 8616 8617 wts.visor.on(UI_EVENTS.VEHICLE_DETAILS_TRACK_DEALER_VIDEO_EVENT, function (e) { 8618 var category = pageData.isVast ? "VAST" : "Vehicle"; 8619 TrackEvent(category, "View Dealer Video", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8620 8621 // Ford Direct Analytics - parallel tracking path 8622 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8623 FordDirectAnalytics.trackClick(e, "VEHICLE_DETAILS_TRACK_DEALER_VIDEO_EVENT"); 8624 } 8625 }); 8626 8627 wts.visor.on(UI_EVENTS.VEHICLE_DETAILS_TRACK_BODY_DETAILS_VIDEO_EVENT, function (e) { 8628 var category = pageData.isVast ? "VAST" : "Vehicle"; 8629 TrackEvent(category, "View Body Details Video", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8630 }); 8631 8632 wts.visor.on(UI_EVENTS.VEHICLE_DETAILS_TRACK_BODY_MANUFACTURER_VIDEO_EVENT, function (e) { 8633 var category = pageData.isVast ? "VAST" : "Vehicle"; 8634 TrackEvent(category, "View Body Manufacturer Video", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8635 }); 8636 8637 wts.visor.on(UI_EVENTS.VEHICLE_DETAILS_CLOSE_VIDEO_EVENT, function (e) { 8638 var category = pageData.isVast ? "VAST" : "Vehicle"; 8639 TrackEvent(category, "Close Video", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8640 8641 const ascEventData = { 8642 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.VIDEO, 8643 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8644 eventActionResult: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.EXIT, 8645 inventoryID: e.data.inventoryID, 8646 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.NAV_ICON, 8647 linkUrl: null, 8648 mediaType: AscTracking.ASC_MAPPINGS.MEDIA_TYPE.VIDEO, 8649 }; 8650 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: ascEventData })); 8651 }); 8652 8653 wts.visor.on(UI_EVENTS.VSP_CHANGE_ITEMS_PER_PAGE, function (e) { 8654 var vehicleCategory = pageData.isVast ? "VAST" : "Search"; 8655 TrackEvent(vehicleCategory, "Items Per Page", e.data.data); 8656 }); 8657 8658 wts.visor.on(UI_EVENTS.VSP_CHANGE_SORT, function (e) { 8659 var vehicleCategory = pageData.isVast ? "VAST" : "Search"; 8660 TrackEvent(vehicleCategory, "Sort By", e.data.data); 8661 }); 8662 8663 wts.visor.on(UI_EVENTS.CLICK_AUCTION_LINK, function (e) { 8664 TrackEvent(vehicleCategory, "View Auction", window.myAnalyticsLabel, window.myAnalyticsDimensions); 8665 }); 8666 8667 wts.visor.on(UI_EVENTS.CLICK_VEHICLE_LISTING_TITLE, function (e) { 8668 var inventoryID = e.data; 8669 8670 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8671 if (pageData.isVast) { 8672 window.TrackEvent(vehicleCategory, "View VAST VDP - Title", label, dimensions); 8673 } 8674 else { 8675 window.TrackEvent(vehicleCategory, "More Info - Title", label, dimensions); 8676 } 8677 }); 8678 });
8679 8680 wts.visor.on(UI_EVENTS.CLICK_VEHICLE_LISTING_PHOTO, function (e) { 8681 var isChassisSide = e.data.isChassisSide; 8682 var inventoryID = e.data.inventoryID; 8683 // when clicking a link, the chassis side has [C] and the body side has [B] 8684 var side = isChassisSide ? "[C]" : "[B]"; 8685 8686 var action = "{action} - Photo {side}".replace("{side}", side); 8687 8688 getLabelAndDimensions(inventoryID, function (labels, dimensions) { 8689 if (pageData.isVast) { 8690 action = action.replace("{action}", "View VAST VDP") 8691 window.TrackEvent(vehicleCategory, action, labels, dimensions); 8692 } 8693 else { 8694 action = action.replace("{action}", "More Info") 8695 window.TrackEvent(vehicleCategory, action, labels, dimensions); 8696 } 8697 }); 8698 }); 8699 8700 wts.visor.on(UI_EVENTS.CLICK_VEHICLE_LISTING_SEE_DETAILS, function (e) { 8701 var inventoryID = e.data; 8702 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8703 if (pageData.isVast) { 8704 window.TrackEvent(vehicleCategory, "View VAST VDP - See More Details", label, dimensions); 8705 } 8706 else { 8707 window.TrackEvent(vehicleCategory, "More Info - See More Details", label, dimensions); 8708 } 8709 }); 8710 }); 8711 8712 wts.visor.on(UI_EVENTS.CLICK_REQUEST_UPFIT, function (e) { 8713 var selectedPackage = e.data; 8714 var label = selectedPackage === null ? "" : selectedPackage; 8715 8716 TrackEvent("RequestAnUpfit", "Request for Upfit With Truck", label); 8717 window.lastEvent = "Request for Upfit With Truck"; 8718 }); 8719 8720 wts.visor.on(UI_EVENTS.CLICK_REQUEST_UPFIT_INFO, function (e) { 8721 var selectedPackage = e.data; 8722 var label = selectedPackage === null ? "" : selectedPackage; 8723 TrackEvent("LookingForAnUpfit", "View More Info", label); 8724 }); 8725 8726 wts.visor.on(UI_EVENTS.CLICK_UPFIT_SPECIFICATION_SHEET, function (e) { 8727 var selectedPackage = e.data; 8728 TrackEvent("LookingForAnUpfit", "Clicked Spec Sheet", selectedPackage); 8729 8730 // Ford Direct Analytics - parallel tracking path 8731 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8732 FordDirectAnalytics.trackToolEvent("download-build-and-get-quote"); 8733 } 8734 }); 8735 8736 wts.visor.on(UI_EVENTS.CLICK_VDP_COMVOY_WEBSITE_LINK, function (e) { 8737 var linkURL = e.data; 8738 TrackEvent("Vehicle", "Click Dealer Website Link", linkURL); 8739 }); 8740 8741 wts.visor.on(UI_EVENTS.SELECT_UPFIT_VOCATION, function (e) { 8742 var vocation = e.data; 8743 TrackEvent("LookingForAnUpfit", "Chose a Vocation", vocation); 8744 }); 8745 8746 wts.visor.on(UI_EVENTS.CLICK_REMOVE_SELECTED_PACKAGE, function (e) { 8747 TrackEvent("LookingForAnUpfit", "Browse Other Upfits"); 8748 }); 8749 8750 wts.visor.on(UI_EVENTS.SELECT_UPFIT_PACKAGE, function (e) { 8751 var selectedPackage = e.data; 8752 TrackEvent("LookingForAnUpfit", "Select Package", selectedPackage); 8753 }); 8754 8755 wts.visor.on(UI_EVENTS.SUBMIT_REQUEST_UPFIT_FORM, function (e) { 8756 var selectedPackage = e.data; 8757 window.TrackEvent("RequestAnUpfit", undefined, selectedPackage); 8758 }); 8759 8760 wts.visor.on(UI_EVENTS.SELECT_VAN_BUILDER_VOCATION, function (e) { 8761 var vocation = e.data; 8762 window.TrackEvent("VanBuilder", "Chose a Vocation", vocation); 8763 }); 8764 8765 wts.visor.on(UI_EVENTS.ABOUT_EVENT, function (e) { 8766 var eventName = e.data.eventName; 8767 var value = e.data.value; 8768 8769 window.TrackEvent("About", eventName, value); 8770 var currentTarget = e.data.value.currentTarget; 8771 8772 if (currentTarget) { 8773 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: currentTarget })); 8774 } 8775 8776 // Ford Direct Analytics - parallel tracking path 8777 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8778 FordDirectAnalytics.trackClick(e, "ABOUT_EVENT"); 8779 } 8780 });
8781 8782 wts.visor.on(UI_EVENTS.EZORDER_EVENT, function (e) { 8783 var category = e.data.category; 8784 var action = e.data.action; 8785 var label = e.data.label; 8786 8787 window.TrackEvent(category, action, label); 8788 8789 ascEventData = { 8790 commType: AscTracking.ASC_MAPPINGS.COMM_TYPE.FORM, 8791 commStatus: action.startsWith("Open") ? AscTracking.ASC_MAPPINGS.COMM_STATUS.START : action.startsWith("Close") ? AscTracking.ASC_MAPPINGS.COMM_STATUS.CLOSE : AscTracking.ASC_MAPPINGS.COMM_STATUS_ENGAGE, 8792 formName: "EZ Order", 8793 currentTarget: e.data.currentTarget 8794 }; 8795 8796 AscTracking.trackFormEngage(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_ENGAGEMENT, { detail: ascEventData })); 8797 }); 8798 8799 wts.visor.on(UI_EVENTS.CUSTOM_ORDER_EVENT, function (e) { 8800 var category = e.data.category; 8801 var action = e.data.action; 8802 var label = e.data.label; 8803 8804 window.TrackEvent(category, action, label); 8805 }); 8806 8807 wts.visor.on(UI_EVENTS.TAYCOR_EVENT, function (e) { 8808 var category = e.data.category; 8809 var action = e.data.action; 8810 var label = e.data.label; 8811 8812 window.TrackEvent(category, action, label); 8813 }); 8814 8815 wts.visor.on(UI_EVENTS.CRESTLINE_EVENT, function (e) { 8816 var category = e.data.category; 8817 var action = e.data.action; 8818 var label = e.data.label; 8819 8820 window.TrackEvent(category, action, label); 8821 8822 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 8823 }); 8824 8825 wts.visor.on(UI_EVENTS.WEBBUY_EVENT, function (e) { 8826 var category = pageData.isVSP ? "Search" : "Vehicle"; 8827 var action = null; 8828 var label = null; 8829 8830 if (e.data) { 8831 var el = e.data; 8832 8833 let title = el.getAttribute("vehicleTitle"); 8834 let vin = el.getAttribute("vin"); 8835 8836 label = title && vin ? title + " - " + vin : label; 8837 8838 switch (el.innerHTML.toLowerCase()) { 8839 case "buy now": 8840 action = "Click WebBuy Buy Now CTA"; 8841 break; 8842 case "instant trade offer": 8843 action = "Click WebBuy Instant Trade Offer CTA"; 8844 break; 8845 case "get financed online": 8846 action = "Click WebBuy Get Financed Online CTA"; 8847 break; 8848 default: 8849 // no-op 8850 break; 8851 } 8852 8853 if (action && label) { 8854 window.TrackEvent(category, action, label); 8855 } 8856 8857 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data })); 8858 } 8859 8860 8861 }); 8862 8863 wts.visor.on(UI_EVENTS.CVSHOWROOM_EVENT, function (e) { 8864 var category = e.data.category; 8865 var action = e.data.action; 8866 var label = e.data.label; 8867 8868 window.TrackEvent(category, action, label); 8869 }); 8870 8871 wts.visor.on(UI_EVENTS.CLICK_CUSTOM_PAGE_EMAIL_DEAILER, function (e) { 8872 var customPageName = e.data.customPageName; 8873 TrackEvent(customPageName, "Email Dealer"); 8874 8875 if (!window.asc_datalayer) { 8876 return; 8877 } 8878 8879 var pageType = window.asc_datalayer.page_type; 8880 if (modules.AscDelegatedTracking) { 8881 pageType = modules.AscDelegatedTracking.resolvePageType(document.body) || pageType; 8882 } 8883 8884 AscTracking.fireAscEvent({ 8885 event: AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, 8886 event_owner: window.asc_datalayer.affiliation, 8887 page_type: pageType, 8888 element_text: "Email Dealer", 8889 element_title: "Email Dealer", 8890 element_type: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.FORM, 8891 element_subtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.CTA_BUTTON, 8892 event_action: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8893 event_action_result: AscTracking.ASC_MAPPINGS.EVENT_ACTION_RESULT.POPUP, 8894 form_name: customPageName 8895 }); 8896 });
8897 8898 wts.visor.on(UI_EVENTS.VEHICLE_DETAILS_TRACK_VEHICLE_EVENT, function (e) { 8899 var eventName = e.data.eventName; 8900 var label = e.data.label; 8901 var dimensions = e.data.dimensions; 8902 TrackEvent("Vehicle", eventName, label, dimensions); 8903 8904 e.data.type = "cta"; 8905 e.data.page = "shop-vehicle-detais"; 8906 e.data.location = "vehicle-container"; 8907 8908 if (eventName === "Print Confirmed") { 8909 e.data.description = "gallery-print"; 8910 } else if (eventName.indexOf("Share - ") > -1) { 8911 e.data.description = "social-share"; 8912 } else { 8913 // if eventName isn't explicitly handled then dont track the adobe event 8914 return; 8915 } 8916 8917 const adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 8918 if (adobePageData) { 8919 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 8920 } 8921 8922 const ascEventData = { 8923 elementText: e.data.eventName, 8924 elementTitle: e.data.elementTitle, 8925 elementSubtype: AscTracking.ASC_MAPPINGS.ELEMENT_SUBTYPE.HYPERLINK, 8926 elementType: AscTracking.ASC_MAPPINGS.ELEMENT_TYPE.MENU, 8927 eventAction: AscTracking.ASC_MAPPINGS.EVENT_ACTION.CLICK, 8928 }; 8929 AscTracking.trackInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MENU_INTERACTION, { detail: ascEventData })); 8930 }); 8931 8932 wts.visor.on(UI_EVENTS.TRACK_CLOSE_FORM_EVENT, function (e) { 8933 let inventoryID = e.data.inventoryID; 8934 let eventName = e.data.eventName; 8935 8936 if (inventoryID) { 8937 getLabelAndDimensions(inventoryID, function (label, dimensions) { 8938 window.TrackEvent("Vehicle", eventName, label, dimensions); 8939 }); 8940 } 8941 else { 8942 window.TrackEvent("Vehicle", eventName); 8943 } 8944 8945 AscTracking.trackFormClose(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_FORM_ENGAGEMENT, { detail: e.data })); 8946 }); 8947 8948 wts.visor.on(UI_EVENTS.VEHICLE_DETAILS_TRACK_BRANDING_EVENT, function (e) { 8949 var eventName = e.data.eventName; 8950 var label = e.data.label; 8951 var dimensions = e.data.dimensions; 8952 TrackEvent("Branding", eventName, label, dimensions); 8953 }); 8954 8955 wts.visor.on(UI_EVENTS.MY_GARAGE_GARAGE_EVENT, function (e) { 8956 var eventName = e.data.eventName; 8957 var value = e.data.value; 8958 TrackEvent("Garage", eventName, value); 8959 }); 8960 8961 wts.visor.on(UI_EVENTS.MY_GARAGE_VEHICLE_EVENT, function (e) { 8962 var eventName = e.data.eventName; 8963 var label = e.data.label; 8964 var dimensions = e.data.dimensions; 8965 TrackEvent("Vehicle", eventName, label, dimensions); 8966 }); 8967 8968 wts.visor.on(UI_EVENTS.INVENTORY_LAYOUT_TRACK_SEARCH_EVENT, function (e) { 8969 var eventName = e.data.eventName; 8970 var value = e.data.value; 8971 8972 window.TrackEvent("Search", eventName, value); 8973 8974 var currentTarget = e.data.value.currentTarget; 8975 if (currentTarget) { 8976 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: currentTarget })); 8977 } 8978 8979 // Ford Direct Analytics - parallel tracking path 8980 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 8981 FordDirectAnalytics.trackClick(e, "INVENTORY_LAYOUT_TRACK_SEARCH_EVENT"); 8982 } 8983 }); 8984 8985 wts.visor.on(UI_EVENTS.INVENTORY_LAYOUT_TRACK_LOCATOR_SEARCH_EVENT, function (e) { 8986 var eventName = e.data.eventName; 8987 var label = e.data.label; 8988 var dimensions = e.data.dimensions; 8989 TrackEvent("Search", eventName, label, dimensions); 8990 }); 8991 8992 wts.visor.on(UI_EVENTS.SELECT_DEALER_LOCATOR_SEARCH, function (e) { 8993 var action = e.data.action; 8994 var label = e.data.label; 8995 var dimensions = e.data.dimensions; 8996 window.TrackEvent("Dealer Locator Search", action, label, dimensions); 8997 }); 8998 8999 wts.visor.on(UI_EVENTS.SELECT_DEALER_LOCATOR_SEARCH_PAGE, function (e) { 9000 var searchPage = e.data; 9001 window.TrackEvent("Dealer Locator Search", "Go To Page", searchPage); 9002 });
9003 9004 wts.visor.on(UI_EVENTS.SELECT_DEALER_LOCATOR_SEARCH_CONDITIONAL, function (e) { 9005 var eventName = e.data.eventName; 9006 var value = e.data.value; 9007 window.TrackEvent("DealerLocatorSearch", eventName, value); 9008 }); 9009 9010 wts.visor.on(UI_EVENTS.TRUCK_SEARCH_EVENT_CONDITIONAL, function (e) { 9011 var eventName = e.data.eventName; 9012 var value = e.data.value; 9013 var category = pageData.isVast ? "VAST" : "Search"; 9014 TrackEvent(category, eventName, value); 9015 9016 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9017 if (adobePageData) { 9018 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9019 } 9020 }); 9021 9022 wts.visor.on(UI_EVENTS.TRUCK_SEARCH_EVENT_VIN, function (e) { 9023 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9024 if (adobePageData) { 9025 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9026 } 9027 }); 9028 9029 wts.visor.on(UI_EVENTS.MILES_SEARCH_CONDITIONAL, function (e) { 9030 var value = e.data.value + " Miles"; 9031 var category = pageData.isVast ? "VAST" : "Search"; 9032 TrackEvent(category, "Distance", value); 9033 9034 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9035 if (adobePageData) { 9036 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9037 } 9038 }); 9039 9040 wts.visor.on(UI_EVENTS.LOCATION_SEARCH_CONDITIONAL, function (e) { 9041 var category = pageData.isVast ? "VAST" : "Search"; 9042 TrackEvent(category, "Location Entry", e.data.value); 9043 }); 9044 9045 wts.visor.on(UI_EVENTS.TRUCK_SEARCH_ZIPCODE_CHANGE, function (e) { 9046 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9047 if (adobePageData) { 9048 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9049 } 9050 }); 9051 9052 wts.visor.on(UI_EVENTS.VOCATION_SEARCH_CONDITIONAL, function (e) { 9053 var value = `Vocation: ${e.data.value}`; 9054 TrackEvent("Search", "Click Value", value); 9055 }); 9056 9057 wts.visor.on(UI_EVENTS.TRUCK_SEARCH_EVENT_NO_RESULTS, function (e) { 9058 var noResultsEventLabel = e.data.noResultsEventLabel; 9059 var category = pageData.isVast ? "VAST" : "Search"; 9060 TrackEvent(category, "No Results", noResultsEventLabel, {}); 9061 }); 9062 9063 wts.visor.on(UI_EVENTS.TRUCK_SEARCH_CLEAR_FILTERS, function (e) { 9064 var category = pageData.isVast ? "VAST" : "Search"; 9065 TrackEvent(category, "Click Clear", "Clear Search Filters"); 9066 9067 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9068 if (adobePageData) { 9069 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9070 } 9071 9072 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9073 }); 9074 9075 wts.visor.on(UI_EVENTS.CLICK_VEHICLE_AD, function (e) { 9076 var label = e.data.label 9077 var dimensions = e.data.dimensions; 9078 TrackEvent(vehicleCategory, "Ad Click", label, dimensions); 9079 9080 AscTracking.trackMediaInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_MEDIA_INTERACTION, { detail: e.data })); 9081 }); 9082 9083 wts.visor.on(UI_EVENTS.CLICK_RECENTLY_VIEWED, function (e) { 9084 var label = e.data.label; 9085 var dimensions = e.data.dimensions; 9086 window.TrackEvent(vehicleCategory, "Recently Viewed Click", label, dimensions); 9087 }); 9088 9089 wts.visor.on(UI_EVENTS.CLICK_TRUCK_SEARCH_FACETS_VAST, function (e) { 9090 var keyValue = e.data.keyValue; 9091 TrackEvent("VAST", "Click Value", keyValue); 9092 }); 9093 9094 wts.visor.on(UI_EVENTS.CLICK_SHOW_MY_INVENTORY_ON_VAST, function () { 9095 TrackEvent("VAST", "Click Value", "IncludeInventory: My Inventory"); 9096 }); 9097 9098 wts.visor.on(UI_EVENTS.CLICK_SHOW_SISTER_INVENTORY_ON_VAST, function () { 9099 TrackEvent("VAST", "Click Value", "IncludeInventory: Sister Inventory"); 9100 });
9101 9102 wts.visor.on(UI_EVENTS.CLICK_SHOW_GROUP_INVENTORY_ON_VAST, function () { 9103 TrackEvent("VAST", "Click Value", "IncludeInventory: Group Inventory"); 9104 }); 9105 9106 wts.visor.on(UI_EVENTS.CLICK_AVAILABLE_FOR_PURCHASE_TAB_ON_VAST, function () { 9107 TrackEvent("VAST", "Click Tab", "Available for Purchase"); 9108 }); 9109 9110 wts.visor.on(UI_EVENTS.CLICK_WHOLE_WTS_NETWORK_TAB_ON_VAST, function () { 9111 TrackEvent("VAST", "Click Tab", "Whole WTS Network"); 9112 }); 9113 9114 wts.visor.on(UI_EVENTS.CLICK_BRIGHTDROP_POOL_TAB_ON_VAST, function () { 9115 TrackEvent("VAST", "Click Tab", "Brightdrop Pool"); 9116 }); 9117 9118 wts.visor.on(UI_EVENTS.OPEN_VEHICLE_ACQUISITION_DETAILS, function (e) { 9119 var inventoryID = e.data; 9120 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9121 TrackEvent("VAST", "View Acquisition Details", label, dimensions); 9122 }); 9123 }); 9124 9125 wts.visor.on(UI_EVENTS.CLICK_EMAIL_VAST_CONTACT, function (e) { 9126 var inventoryID = e.data; 9127 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9128 TrackEvent("VAST", "Email Acquisition Contact", label, dimensions); 9129 }); 9130 }); 9131 9132 wts.visor.on(UI_EVENTS.CLICK_CALL_VAST_CONTACT, function (e) { 9133 var inventoryID = e.data; 9134 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9135 TrackEvent("VAST", "Call Acquisition Contact", label, dimensions); 9136 }); 9137 }); 9138 9139 wts.visor.on(UI_EVENTS.CLICK_CALL_VAST_DEALER, function (e) { 9140 var inventoryID = e.data; 9141 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9142 TrackEvent("VAST", "Call Dealer", label, dimensions); 9143 }); 9144 }); 9145 9146 wts.visor.on(UI_EVENTS.CLICK_VAST_CONDITION_LINK, function (e) { 9147 var inventoryID = e.data; 9148 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9149 TrackEvent("VAST", "View Condition", label, dimensions); 9150 }); 9151 }); 9152 9153 wts.visor.on(UI_EVENTS.CLICK_VAST_AUCTION_LINK, function (e) { 9154 var inventoryID = e.data; 9155 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9156 TrackEvent("VAST", "View Auction", label, dimensions); 9157 }); 9158 }); 9159 9160 wts.visor.on(UI_EVENTS.SUBMIT_VAST_MESSAGE, function (e) { 9161 var inventoryID = e.data; 9162 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9163 TrackEvent("VAST", "Message Acquisition Contact", label, dimensions); 9164 }); 9165 }); 9166 9167 wts.visor.on(UI_EVENTS.CLICK_VAST_OUTBOUND_LINK, function (e) { 9168 var inventoryID = e.data.inventoryID; 9169 var href = e.data.href; 9170 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9171 window.TrackEvent("VAST", "View Outbound Link", href, dimensions); 9172 }); 9173 }); 9174 9175 wts.visor.on(UI_EVENTS.CLICK_BACK_TO_SEARCH_RESULTS, function (e) { 9176 var inventoryID = e.data.inventoryID; 9177 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9178 window.TrackEvent(vehicleCategory, "Close Details", label, dimensions); 9179 }); 9180 9181 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9182 if (adobePageData) { 9183 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9184 } 9185 }); 9186 9187 wts.visor.on(UI_EVENTS.CLICK_BARE_CHASSIS_INQUIRY, function (e) { 9188 var inventoryID = e.data; 9189 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9190 window.TrackEvent(vehicleCategory, "Bare Chassis Request", label, dimensions); 9191 }); 9192 }); 9193 9194 wts.visor.on(UI_EVENTS.CLICK_BODY_ONLY_INQUIRY, function (e) { 9195 var inventoryID = e.data; 9196 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9197 window.TrackEvent(vehicleCategory, "Body Only Request", label, dimensions); 9198 }); 9199 }); 9200 9201 wts.visor.on(UI_EVENTS.CLICK_SAVE_VEHICLE_TO_GARAGE, function (e) { 9202 var inventoryID = e.data; 9203 getLabelAndDimensions(inventoryID, function (label, dimensions) { 9204 window.TrackEvent(vehicleCategory, "Save To Garage", label, dimensions); 9205 }); 9206 });
9207 9208 9209 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_STATE_OPEN_EVENT, function (e) { 9210 var label = e.data.label; 9211 var category = pageData.isVast ? "VAST" : "Search"; 9212 window.TrackEvent(category, "Open Facet Header", label); 9213 9214 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9215 if (adobePageData) { 9216 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9217 } 9218 9219 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9220 }); 9221 9222 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_STATE_CLOSE_EVENT, function (e) { 9223 var label = e.data.label; 9224 var category = pageData.isVast ? "VAST" : "Search"; 9225 window.TrackEvent(category, "Close Facet Header", label); 9226 9227 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9228 if (adobePageData) { 9229 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9230 } 9231 9232 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9233 }); 9234 9235 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_CHASSIS_OPEN_EVENT, function (e) { 9236 var label = e.data.label; 9237 var category = pageData.isVast ? "VAST" : "Search"; 9238 window.TrackEvent(category, "Open Facet Header", label); 9239 9240 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9241 if (adobePageData) { 9242 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9243 } 9244 9245 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9246 }); 9247 9248 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_CHASSIS_CLOSE_EVENT, function (e) { 9249 var label = e.data.label; 9250 var category = pageData.isVast ? "VAST" : "Search"; 9251 window.TrackEvent(category, "Close Facet Header", label); 9252 9253 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9254 if (adobePageData) { 9255 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9256 } 9257 9258 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9259 }); 9260 9261 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_BODY_OPEN_EVENT, function (e) { 9262 var label = e.data.label; 9263 var category = pageData.isVast ? "VAST" : "Search"; 9264 window.TrackEvent(category, "Open Facet Header", label); 9265 9266 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9267 if (adobePageData) { 9268 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9269 } 9270 9271 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9272 }); 9273 9274 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_BODY_CLOSE_EVENT, function (e) { 9275 var label = e.data.label; 9276 var category = pageData.isVast ? "VAST" : "Search"; 9277 window.TrackEvent(category, "Close Facet Header", label); 9278 9279 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9280 if (adobePageData) { 9281 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9282 } 9283 9284 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9285 }); 9286 9287 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_ADDONS_OPEN_EVENT, function (e) { 9288 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9289 if (adobePageData) { 9290 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9291 } 9292 9293 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9294 });
9295 9296 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_ADDONS_CLOSE_EVENT, function (e) { 9297 var adobePageData = AdobeAnalytics.getAdobeInteractionClickEventData(e); 9298 if (adobePageData) { 9299 trackAdobeEvent(new CustomEvent("interaction-click", { detail: adobePageData })); 9300 } 9301 9302 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9303 }); 9304 9305 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_PROGRESSIVE_OPEN_EVENT, function (e) { 9306 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9307 }); 9308 9309 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_PROGRESSIVE_CLOSE_EVENT, function (e) { 9310 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9311 }); 9312 9313 wts.visor.on(UI_EVENTS.VSP_FACET_OPEN_EVENT, function (e) { 9314 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9315 }); 9316 9317 wts.visor.on(UI_EVENTS.VSP_FACET_CLOSE_EVENT, function (e) { 9318 AscTracking.trackGeneralLayoutInteractionClick(new CustomEvent(AscTracking.ASC_MAPPINGS.EVENT.ASC_CTA_INTERACTION, { detail: e.data.currentTarget })); 9319 }); 9320 9321 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_TRAILER_OPEN_EVENT, function (e) { 9322 var label = e.data.label; 9323 var category = pageData.isVast ? "VAST" : "Search"; 9324 window.TrackEvent(category, "Open Facet Header", label); 9325 }); 9326 9327 wts.visor.on(UI_EVENTS.VSP_FACETGROUP_TRAILER_CLOSE_EVENT, function (e) { 9328 var label = e.data.label; 9329 var category = pageData.isVast ? "VAST" : "Search"; 9330 window.TrackEvent(category, "Close Facet Header", label); 9331 }); 9332 9333 wts.visor.on(UI_EVENTS.VAST_VSP_FACETGROUP_PARTNER_OPEN_EVENT, function (e) { 9334 if (pageData.isVast) { 9335 var label = e.data.label; 9336 window.TrackEvent("VAST", "Open Facet Header", label); 9337 } 9338 }); 9339 9340 wts.visor.on(UI_EVENTS.VAST_VSP_FACETGROUP_PARTNER_CLOSE_EVENT, function (e) { 9341 if (pageData.isVast) { 9342 var label = e.data.label; 9343 window.TrackEvent("VAST", "Close Facet Header", label); 9344 } 9345 }); 9346 9347 wts.visor.on(UI_EVENTS.NAVIGATION_PAGE_PARTNER_LEARN_MORE, function (e) { 9348 var label = `${getClientData("#dealer-data").dealerDescription}-Click Nav Page Partner Ad Learn More`; 9349 window.TrackEvent("Dealer Tools", "Navigation Page Clicks", label); 9350 }); 9351 9352 wts.visor.on(UI_EVENTS.NAVIGATION_PAGE_VIEW_ALL_PARTNERS, function (e) { 9353 var label = `${getClientData("#dealer-data").dealerDescription}-Click Nav Page View All Partners`; 9354 window.TrackEvent("Dealer Tools", "Navigation Page Clicks", label); 9355 }); 9356 9357 wts.visor.on(UI_EVENTS.MARKETPLACE_EVENT, function (e) { 9358 var category = e.data.category; 9359 var action = e.data.action; 9360 var label = e.data.label; 9361 9362 window.TrackEvent(category, action, label); 9363 }); 9364 9365 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_TOGGLE, function (e) { 9366 window.TrackEvent("Catalog", "Commercial Catalog Toggle", e.data); 9367 }); 9368 9369 9370 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_VIEW_BODY_LINE_LITERATURE_PDF, function (e) { 9371 window.TrackEvent("Catalog", "View Body Line Literature PDF", "Click " + e.data.Target, { dimension9: e.data.BodyType } ); 9372 9373 // Ford Direct Analytics - parallel tracking path 9374 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 9375 FordDirectAnalytics.trackToolEvent("download-commercial-catalog"); 9376 } 9377 }); 9378 9379 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_VIEW_BODY_LINE_VIDEO, function (e) { 9380 window.TrackEvent("Catalog", "View Body Line Video", "Click " + e.data.BodyLine + " " + e.data.Source, { dimension9: e.data.BodyType }); 9381 9382 // Ford Direct Analytics - parallel tracking path 9383 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 9384 FordDirectAnalytics.trackClick(e, "COMMERCIAL_CATALOG_VIEW_BODY_LINE_VIDEO"); 9385 } 9386 });
9387 9388 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_START_REQUEST, function (e) { 9389 window.TrackEvent("Catalog", "Start Request", "Start " + e.data.BodyLine + " " + e.data.BodyType + " Custom Order Request", { dimension9: e.data.BodyType }); 9390 }); 9391 9392 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_VIEW_PHOTO, function (e) { 9393 window.TrackEvent("Catalog", "View Photo", "Click Photo " + e.data.BodyLine + " " + e.data.Source , { dimension9: e.data.BodyType }); 9394 9395 // Ford Direct Analytics - parallel tracking path 9396 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 9397 FordDirectAnalytics.trackClick(e, "COMMERCIAL_CATALOG_VIEW_PHOTO"); 9398 } 9399 }); 9400 9401 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_NEXT_PHOTO, function (e) { 9402 window.TrackEvent("Catalog", "View Next Photo", "View Next Photo " + e.data.BodyLine + ' - ' + e.data.URL, { dimension9: e.data.BodyType }); 9403 }); 9404 9405 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_PREV_PHOTO, function (e) { 9406 window.TrackEvent("Catalog", "View Previous Photo", "View Next Photo " + e.data.BodyLine + ' - ' + e.data.URL, { dimension9: e.data.BodyType }); 9407 }); 9408 9409 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_CHANGE_BODY_TYPE, function (e) { 9410 window.TrackEvent("Catalog", "Change Body Type", "Click " + e.data, { dimension9: e.data}); 9411 }); 9412 9413 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_CLICK_BODY_LINE, function (e) { 9414 window.TrackEvent("Catalog", "Select Body Line", "Click " + e.data.BodyManufacturer + " " + e.data.BodyLine, { dimension9: e.data.BodyType }); 9415 }); 9416 9417 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_NEXT, function (e) { 9418 window.TrackEvent("Catalog", "Page Thumbnails Next", "Page Thumbnails Next " + e.data.BodyLine, { dimension9: e.data.BodyType }); 9419 9420 // Ford Direct Analytics - parallel tracking path 9421 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 9422 FordDirectAnalytics.trackClick(e, "COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_NEXT"); 9423 } 9424 }); 9425 9426 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_PREVIOUS, function (e) { 9427 window.TrackEvent("Catalog", "Page Thumbnails Previous", "Page Thumbnails Previous " + e.data.BodyLine, { dimension9: e.data.BodyType }); 9428 9429 // Ford Direct Analytics - parallel tracking path 9430 if (FordDirectAnalytics && FordDirectAnalytics.isEnabled()) { 9431 FordDirectAnalytics.trackClick(e, "COMMERCIAL_CATALOG_CLICK_PAGE_THUMBNAILS_PREVIOUS"); 9432 } 9433 }); 9434 9435 wts.visor.on(UI_EVENTS.COMMERCIAL_CATALOG_CLICK_SHOW_MORE, function (e) { 9436 window.TrackEvent("Catalog", "Show More", "Show More" + " " + e.data.BodyLine, {dimension9: e.data.BodyType}); 9437 }); 9438 9439 wts.visor.on(UI_EVENTS.CONDITIONAL_CALL_TO_ACTION, function (e) { 9440 if (e.data) { 9441 var el = e.data; 9442 let category = el.getAttribute("category"); 9443 let action = el.getAttribute("action"); 9444 let label = el.getAttribute("label"); 9445 9446 window.TrackEvent(category, action, label); 9447 } 9448 }); 9449 9450 wts.visor.on(UI_EVENTS.RESOURCE_RACK_LAUNCH_PAGE, function (e) { 9451 window.TrackEvent("Resource Rack", "Navigation Launch", null, { ...e.data, ActionSubType: 'Navigation Launch Resource Rack' }); 9452 }); 9453 9454 wts.visor.on(UI_EVENTS.RESOURCE_RACK_CHANGE_SEARCH_CONTROLS, function (e) { 9455 window.TrackEvent("Resource Rack", "Search", null, e.data ); 9456 }); 9457 9458 wts.visor.on(UI_EVENTS.RESOURCE_RACK_PREVIEW_LINK_CLICK, function (e) { 9459 window.TrackEvent("Resource Rack", "Preview Link Click", null, e.data); 9460 }); 9461 9462 wts.visor.on(UI_EVENTS.RESOURCE_RACK_ACTION_MENU_GET_LINK, function (e) { 9463 window.TrackEvent("Resource Rack", "Get Link", null, e.data); 9464 }); 9465 9466 wts.visor.on(UI_EVENTS.RESOURCE_RACK_MULTISELECT_FOOTER_ACTION_COPY_LINKS, function (e) { 9467 window.TrackEvent("Resource Rack", "Copy Links", null, e.data); 9468 }); 9469 9470 wts.visor.on(UI_EVENTS.RESOURCE_RACK_MULTISELECT_FOOTER_ACTION_START_PROSPECTING, function (e) { 9471 window.TrackEvent("Resource Rack", "Start Prospecting", null, e.data); 9472 });
9473 9474 9475 wts.visor.on(UI_EVENTS.TRACKING_LINKS_HISTORY_LAUNCH_PAGE, function (e) { 9476 window.TrackEvent("Tracking Links History", "Navigation Launch", null, { ...e.data, Version: 'V1' }); 9477 }); 9478 //Create and submit have been hidden from page for time being 9479 //wts.visor.on(UI_EVENTS.TRACKING_LINKS_HISTORY_CREATE_TRACKING_LINK, function (e) { 9480 // window.TrackEvent("Tracking Links History", "Launch Create Tracking Link"); 9481 //}); 9482 9483 //wts.visor.on(UI_EVENTS.TRACKING_LINKS_HISTORY_SUBMIT_TRACKING_LINK, function (e) { 9484 // window.TrackEvent("Tracking Links History", "Create Tracking Link Submit"); 9485 //}); 9486 9487 wts.visor.on(UI_EVENTS.TRACKING_LINKS_HISTORY_CLICK_DETAILS, function (e) { 9488 window.TrackEvent("Tracking Links History", "View Details", null, {...e.data, Version: 'V1'}); 9489 }); 9490 9491 wts.visor.on(UI_EVENTS.TRACKING_LINKS_HISTORY_CLICK_EDIT_TRACKING_LINK, function (e) { 9492 window.TrackEvent("Tracking Links History", "Launch Edit Link", null, { ...e.data, Version: 'V1' }); 9493 }); 9494 9495 wts.visor.on(UI_EVENTS.TRACKING_LINKS_HISTORY_CLICK_SAVE_EDIT, function (e) { 9496 window.TrackEvent("Tracking Links History", "Edit Link Submit", null, { ...e.data, Version: 'V1' }); 9497 }); 9498 9499 wts.visor.on(UI_EVENTS.TRACKING_LINKS_HISTORY_CLICK_DISABLE_LINK, function (e) { 9500 window.TrackEvent("Tracking Links History", "Disable Link", null, { ...e.data, Version: 'V1' }); 9501 }); 9502 9503 wts.visor.on(UI_EVENTS.TRACKING_LINKS_HISTORY_CLICK_COPY_LINK, function (e) { 9504 window.TrackEvent("Tracking Links History", "Copy Link", null, { ...e.data, Version: 'V1' }); 9505 }); 9506 9507 ////Todo - Investigate AddTrackingInfo already present in Email 9508 //wts.visor.on(UI_EVENTS.TRACKING_LINKS_EMAIL_CLICK_SEE_LINKS, function (e) { 9509 // window.TrackEvent("Tracking Links Email", "See Tracking Links"); 9510 //}); 9511 9512 9513 wts.visor.on(UI_EVENTS.INVOICE_BINDER_LAUNCH_PAGE, function (e) { 9514 window.TrackEvent("Invoice Binder", "Navigation Launch", null, { ...e.data, Version: 'V1' }); 9515 }); 9516 9517 wts.visor.on(UI_EVENTS.INVOICE_BINDER_SEARCH, function (e) { 9518 window.TrackEvent("Invoice Binder", "Search", null, { ...e.data, Version: 'V1' }); 9519 }); 9520 9521 wts.visor.on(UI_EVENTS.INVOICE_BINDER_CLICK_ON_COUNT, function (e) { 9522 window.TrackEvent("Invoice Binder", "Click Missing Invoices", null, { ...e.data, Version: 'V1' }); 9523 }); 9524 9525 wts.visor.on(UI_EVENTS.INVOICE_BINDER_CLICK_VIEW_LISTING_DETAILS, function (e) { 9526 window.TrackEvent("Invoice Binder", "View Listing", null, { ...e.data, Version: 'V1' }); 9527 }); 9528 9529 wts.visor.on(UI_EVENTS.INVOICE_BINDER_CLICK_MARK_IF_NOT_UPFIT, function (e) { 9530 window.TrackEvent("Invoice Binder", "Mark it Not Upfit", null, { ...e.data, Version: 'V1' }); 9531 }); 9532 9533 wts.visor.on(UI_EVENTS.INVOICE_BINDER_CLICK_WINDOW_STICKER, function (e) { 9534 window.TrackEvent("Invoice Binder", "View Window Sticker", null, { ...e.data, Version: 'V1' }); 9535 }); 9536 9537 wts.visor.on(UI_EVENTS.INVOICE_BINDER_CLICK_INVOICE_LINK, function (e) { 9538 window.TrackEvent("Invoice Binder", "View Invoice", null, { ...e.data, Version: 'V1' }); 9539 }); 9540 9541 wts.visor.on(UI_EVENTS.INVOICE_BINDER_CLICK_UPLOAD_INVOICES, function (e) { 9542 window.TrackEvent("Invoice Binder", "Upload Invoice", null, { ...e.data, Version: 'V1' }); 9543 }); 9544 9545 wts.visor.on(UI_EVENTS.INVOICE_BINDER_CLICK_DELETE_INVOICE, function (e) { 9546 window.TrackEvent("Invoice Binder", "Delete Invoice", null, { ...e.data, Version: 'V1' }); 9547 }); 9548 9549 wts.visor.on(UI_EVENTS.PRINTING_ACTION_PRINT_COMPLETE_LISTING, function (e) { 9550 window.TrackEvent("Print Listing", "Print Complete Listing", null, { ...e.data, Version: 'V1' }); 9551 }); 9552 9553 wts.visor.on(UI_EVENTS.PRINTING_ACTION_PRINT_UPFIT_WINDOW_STICKER, function (e) { 9554 window.TrackEvent("Print Window Sticker", "Print Upfit Window Sticker", null, { ...e.data, Version: 'V1' }); 9555 }); 9556 9557 /** 9558 * Inventory Management 9559 */ 9560 // General 9561 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_GROUP_SELECT_MEMBER_DEALER, function (e) { 9562 window.TrackEvent("Inventory Management", "General", null, { ...e.data, Version: 'V1' });
9563 }); 9564 9565 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_EXPORT_LISTINGS, function (e) { 9566 window.TrackEvent("Inventory Management", "General", null, { ...e.data, Version: 'V1' }); 9567 }); 9568 9569 // Create Listing 9570 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CREATE_BODY_ONLY_LISTING, function (e) { 9571 window.TrackEvent("Inventory Management", "Create Listing", null, { ...e.data, Version: 'V1' }); 9572 }); 9573 9574 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CREATE_TRAILER_LISTING, function (e) { 9575 window.TrackEvent("Inventory Management", "Create Listing", null, { ...e.data, Version: 'V1' }); 9576 }); 9577 9578 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CREATE_VEHICLE_LISTING, function (e) { 9579 window.TrackEvent("Inventory Management", "Create Listing", null, { ...e.data, Version: 'V1' }); 9580 }); 9581 9582 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_TOTAL_LISTINGS, function (e) { 9583 window.TrackEvent("Inventory Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9584 }); 9585 9586 // Inventory Summary 9587 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_ON_SALE_LISTINGS, function (e) { 9588 window.TrackEvent("Inventory Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9589 }); 9590 9591 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_FEATURED_LISTINGS, function (e) { 9592 window.TrackEvent("Inventory Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9593 }); 9594 9595 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_HIDDEN_LISTINGS, function (e) { 9596 window.TrackEvent("Inventory Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9597 }); 9598 9599 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_PRIVATE_LISTINGS, function (e) { 9600 window.TrackEvent("Inventory_Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9601 }); 9602 9603 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_VAST_ONLY_LISTINGS, function (e) { 9604 window.TrackEvent("Inventory_Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9605 }); 9606 9607 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_UNKNOWN_IF_UPFIT, function (e) { 9608 window.TrackEvent("Inventory_Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9609 }); 9610 9611 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_MISSING_PHOTOS, function (e) { 9612 window.TrackEvent("Inventory_Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9613 }); 9614 9615 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_INVALID_PHOTOS, function (e) { 9616 window.TrackEvent("Inventory_Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9617 }); 9618 9619 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_NO_INVOICES, function (e) { 9620 window.TrackEvent("Inventory_Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9621 }); 9622 9623 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_MANUAL_LISTINGS, function (e) { 9624 window.TrackEvent("Inventory_Management", "Inventory Summary", null, { ...e.data, Version: 'V1' }); 9625 }); 9626 9627 // Inventory Search 9628 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_RUN_LAST_QUERY, function (e) { 9629 window.TrackEvent("Inventory Management", "Inventory Search", null, { ...e.data, Version: 'V1' }); 9630 }); 9631 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_TOGGLE_ADVANCED_SEARCH, function (e) { 9632 window.TrackEvent("Inventory Management", "Inventory Search", null, { ...e.data, Version: 'V1' }); 9633 }); 9634 9635 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_TEXT_SEARCH, function (e) { 9636 window.TrackEvent("Inventory Management", "Inventory Search", null, { ...e.data, Version: 'V1' }); 9637 }); 9638 9639 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_FILTER_INVENTORY_SELECT, function (e) { 9640 window.TrackEvent("Inventory Management", "Inventory Search", null, { ...e.data, Version: 'V1' }); 9641 }); 9642 9643 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_FILTER_INVENTORY_CHECKBOX, function (e) { 9644 window.TrackEvent("Inventory Management", "Inventory Search", null, { ...e.data, Version: 'V1' });
9645 }); 9646 9647 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_RESET_FILTERS, function (e) { 9648 window.TrackEvent("Inventory Management", "Inventory Search", null, { ...e.data, Version: 'V1' }); 9649 }); 9650 9651 // Action Menu 9652 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_VIEW_INVOICES, function (e) { 9653 window.TrackEvent("Inventory Management", "Action Menu", null, { ...e.data, Version: 'V1' }); 9654 }); 9655 9656 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_EDIT_PRICING, function (e) { 9657 window.TrackEvent("Inventory Management", "Action Menu", null, { ...e.data, Version: 'V1' }); 9658 }); 9659 9660 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_EDIT_TRADE_DETAILS, function (e) { 9661 window.TrackEvent("Inventory Management", "Action Menu", null, { ...e.data, Version: 'V1' }); 9662 }); 9663 9664 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_EDIT_LISTING, function (e) { 9665 window.TrackEvent('Inventory Management', 'Action Menu', null, { ...e.data, Version: 'V1' }); 9666 }); 9667 9668 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_MARK_AS_MOVED, function (e) { 9669 window.TrackEvent('Inventory Management', 'Action Menu', null, { ...e.data, Version: 'V1' }); 9670 }); 9671 9672 wts.visor.on(UI_EVENTS.INVENTORY_MANAGEMENT_CLICK_DELETE_LISTING, function (e) { 9673 window.TrackEvent('Inventory Management', 'Action Menu', null, { ...e.data, Version: 'V1' }); 9674 }); 9675 9676 wts.visor.on(UI_EVENTS.VIDEO_PLAYER_START, function (e) { 9677 window.TrackEvent('Youtube Video Player Start', 'VDP', e.data.label, { ...e.data }); 9678 }); 9679 9680 wts.visor.on(UI_EVENTS.VIDEO_PLAYER_RESUME, function (e) {
9681 window.TrackEvent('Youtube Video Player Resume', 'VDP', e.data.label, { ...e.data }); 9682 }); 9683 9684 wts.visor.on(UI_EVENTS.VIDEO_PLAYER_PAUSE, function (e) { 9685 window.TrackEvent('Youtube Video Player Pause', 'VDP', e.data.label, { ...e.data }); 9686 }); 9687 9688 wts.visor.on(UI_EVENTS.VIDEO_PLAYER_THRESHOLD, function (e) { 9689 window.TrackEvent('Youtube Video Player Threshold', 'VDP', e.data.label, { ...e.data }); 9690 }); 9691 9692 wts.visor.on(UI_EVENTS.READ_MORE_VEHICLE_NOTES_CLICK, function (e) { 9693 getLabelAndDimensions(e.data, function (label, dimensions) { 9694 window.TrackEvent("Vehicle", "Read More Vehicle Notes", label, dimensions); 9695 }); 9696 }); 9697 9698 wts.visor.on(UI_EVENTS.READ_LESS_VEHICLE_NOTES_CLICK, function (e) { 9699 getLabelAndDimensions(e.data, function (label, dimensions) { 9700 window.TrackEvent("Vehicle", "Read Less Vehicle Notes", label, dimensions); 9701 }); 9702 }); 9703 9704 wts.visor.on(UI_EVENTS.VIEW_ALL_VEHICLE_OPTIONS_CLICK, function (e) { 9705 getLabelAndDimensions(e.data, function (label, dimensions) { 9706 window.TrackEvent("Vehicle", "View All Vehicle Options", label, dimensions); 9707 }); 9708 }); 9709 9710 wts.visor.on(UI_EVENTS.SEE_DEALERSHIPS_OTHER_LISTINGS_CLICK, function (e) { 9711 getLabelAndDimensions(e.data, function (label, dimensions) { 9712 window.TrackEvent("Vehicle", "See Dealership's Other Listings", label, dimensions); 9713 }); 9714 }); 9715 9716 //Todo - Hook these up to PDF Viewer without wts context 9717 //wts.visor.on(UI_EVENTS.PDF_VIEWER_INTERNAL_LOADING, function (e) { 9718 // window.TrackEvent("PDF Viewer", "Internal PDF Preview", null, { ...e.data, Version: 'V1' }); 9719 //}); 9720 9721 //wts.visor.on(UI_EVENTS.PDF_VIEWER_CLICK_GET_SHAREABLE_LINK, function (e) { 9722 // window.TrackEvent("PDF Viewer", "Get Shareable Link", null, { ...e.data, Version: 'V1' }); 9723 //}); 9724 9725 //wts.visor.on(UI_EVENTS.PDF_VIEWER_CLICK_GET_TRACKING_LINK, function (e) { 9726 // window.TrackEvent("PDF Viewer", "Get Tracking Link", null, { ...e.data, Version: 'V1' }); 9727 //}); 9728 9729 9730 /** 9731 * Hides away the details of fetching the label and dimensions 9732 * 9733 * @description The VDP and VSP fetch the labels and dimensions from different window-attached objects. 9734 * - VDP retrieves it directly via myAnalyticsLabel/myAnalyticsDimensions
9735 * - VSP uses a lookup table via analyticsLabels/analyticsDimensions 9736 * @param {string} inventoryID an inventoryID 9737 * @param {Function} callback A function that will have two parameters: label, dimensions 9738 * @returns {undefined} 9739 */ 9740 function getLabelAndDimensions(inventoryID, callback) { 9741 9742 if (!inventoryID) { 9743 return; 9744 } 9745 9746 var label = ""; 9747 var vin = null; 9748 var customDimensions = null; 9749 9750 // vsp 9751 if (window.analyticsLabels && inventoryID in window.analyticsLabels) { 9752 label += window.analyticsLabels[inventoryID]; 9753 } 9754 9755 // vdp 9756 if (window.myAnalyticsLabel) { 9757 label += window.myAnalyticsLabel; 9758 } 9759 9760 if (window.myAnalyticsDimensions) { 9761 customDimensions = window.myAnalyticsDimensions; 9762 vin = customDimensions["dimension6"] || null; 9763 } 9764 9765 // Order matter a lot here. window.myAnalyticsDimensions is always going to be defined so we will check for the VSP specific version 9766 // to override the previous if statement 9767 if (window.analyticsDimensions && inventoryID in window.analyticsDimensions) { 9768 customDimensions = window.analyticsDimensions[inventoryID]; 9769 vin = customDimensions["dimension6"] || null; 9770 } 9771 9772 if (vin !== null) { 9773 label += " - " + vin; 9774 } 9775 9776 callback(label, customDimensions); 9777 } 9778})(window.wts, window.wts.modules); 9779; 9780wts.registerBundle("marketing"); 9781(function (wts, modules) { 9782 9783 /** 9784 * @module MarketingEvents 9785 * */ 9786 9787 /** 9788 * @const {object} */ 9789 var events = { 9790 vehicleLead: "Vehicle:Lead", 9791 vehicleView: "Vehicle:View" 9792 }; 9793 9794 /** 9795 * Exposes an api to attach event handlers for marketing related events. 9796 * Exposes an api to trigger marketing related events. 9797 * @class MarketingEvents 9798 * @param {EventEmitter} eventEmitter An instance of `EventEmitter` @see {module:EventEmitter} 9799 * @returns {MarketingEvents} An instance of `MarketingEvents` 9800 * @example 9801 * modules.MarketingEvents.onVehicleLead(function(eventArgs) {//Do important things with eventArgs.data}); 9802 * @example 9803 * var inventoryId = 12345; 9804 * modules.MarketingEvents.vehicleLead(new modules.VehicleLeadData(inventoryId)); 9805 * */ 9806 function MarketingEvents(eventEmitter) { 9807 if (!(eventEmitter instanceof modules.EventEmitter)) { 9808 throw "eventEmitter must be an `EventEmitter`"; 9809 } 9810 9811 this.eventEmitter = eventEmitter; 9812 9813 return this; 9814 } 9815 9816 /** 9817 * Handles the vehicle lead event 9818 * @callback VehicleLeadCallback 9819 * @param {Object} eventArgs where eventArgs.data is `VehicleLeadData` 9820 * */ 9821 9822 /** 9823 * Handles the vehicle viewed event 9824 * @callback VehicleViewCallback 9825 * @param {Object} eventArgs where eventArgs.data is `VehicleViewData` 9826 * */ 9827 9828 9829 9830 MarketingEvents.prototype = { 9831 /** 9832 * @description the collection of active marketing event related promises 9833 * @type {Array<Jquery.Deferred>} 9834 */ 9835 activePromises: [], 9836 9837 /** 9838 * @description the collection of inventory details request promises, keyed to inventory id. 9839 * @type {Array<JQuery.Deferred<VehicleViewData>>} 9840 */ 9841 inventoryPromises: [], 9842 9843 /** 9844 * @description returns a Deferred (@see Jquery.Deferred) that resolves when all activePromises (from marketing events) have resolved. 9845 * @returns {JQuery.Deferred} the deferred 9846 */ 9847 wait: function() { 9848 return $.when.apply($, this.activePromises); 9849 }, 9850 9851 /** 9852 * Adds an event handler callback for the `VehicleLead` event 9853 * @param {VehicleLeadCallback} callback - the event handler 9854 * @param {any} context the context to apply the event handler (aka 'this' in the handler scope) 9855 * @returns {void} 9856 * */ 9857 onVehicleLead: function (callback, context) { 9858 this.eventEmitter.on(events.vehicleLead, callback, context); 9859 }, 9860 9861 /** 9862 * Adds an event handler for the `VehicleView` event 9863 * @param {VehicleViewCallback} callback - the event handler 9864 * @param {any} context the context to apply the event handler (aka 'this' in the handler scope) 9865 * @returns {void} 9866 */ 9867 onVehicleView: function (callback, context) { 9868 this.eventEmitter.on(events.vehicleView, callback, context); 9869 }, 9870 9871 /** 9872 * Triggers the `VehicleLead` event 9873 * @param {VehicleLeadData} vehicleLeadData the `VehicleLeadData` event object 9874 * @throws {Error} if parameter not an instance of VehicleLeadData 9875 * @returns {void} 9876 */ 9877 vehicleLead: function (vehicleLeadData) { 9878 if (vehicleLeadData instanceof VehicleLeadData) { 9879 this.eventEmitter.trigger(events.vehicleLead, vehicleLeadData); 9880 } 9881 else { 9882 throw "parameter vehicleLeadData is not an instance of `VehicleLeadData`"; 9883 } 9884 }, 9885 9886 /** 9887 * Triggers the `VehicleView` event 9888 * @param {VehicleViewData} vehicleViewData the `VehicleViewData` event object 9889 * @throws {Error} if parameter not an instance of VehicleViewData 9890 * @returns {void} 9891 */ 9892 vehicleView: function (vehicleViewData) { 9893 if (vehicleViewData instanceof VehicleViewData) { 9894 this.eventEmitter.trigger(events.vehicleView, vehicleViewData); 9895 } 9896 else { 9897 throw "parameter vehicleViewData is not an instance of `VehicleViewData`"; 9898 } 9899 }, 9900 9901 /** 9902 * retrieves the `VehicleViewData` from an api endpoint and caches it as a promise resolving to the data 9903 * @param {string} inventoryId the inventory id for the vehicle 9904 * @param {Function} callback the function to call after the vehicle data is retrieved 9905 * @returns {Promise} the vehicle instance `VehicleViewData` 9906 */ 9907 getVehicleData: function (inventoryId, callback) { 9908 var that = this; 9909 9910 // Create and cache the request promise 9911 if (!this.inventoryPromises[inventoryId]) { 9912 this.inventoryPromises[inventoryId] = $.get("/api/marketing/vehicledata/" + inventoryId) 9913 .fail(function (resp) { 9914 // Do we have a standardized way of handling these things?? 9915 }); 9916 } 9917 9918 // capture the promise that resolves after the callback into activePromises and remove it after the callback 9919 var def = this.inventoryPromises[inventoryId].then(function(data) { 9920 callback(data); 9921 that.activePromises = that.activePromises.filter(function (value) { 9922 return value !== def; 9923 }); 9924 }); 9925 9926 this.activePromises.push(def); 9927 } 9928 }; 9929 9930 /** 9931 * VehicleLeadData holds information relevant to the vehicle lead event 9932 * @class VehicleLeadData 9933 * @param {string} inventoryId - the vehicle inventory id 9934 * @returns {VehicleLeadData} an instance of `VehicleLeadData` 9935 */ 9936 function VehicleLeadData(inventoryId) { 9937 this.inventoryId = inventoryId; 9938 9939 return this; 9940 } 9941 9942 /** 9943 * @class VehicleViewData 9944 * @param {string} inventoryId the vehicle's inventory id 9945 * @param {number} dealerNumericId the vehicle's dealer numeric id 9946 * @param {string} zipCode the dealer's zipcode 9947 * @param {string} make the vehicle's make, e.g. Ford 9948 * @param {string} model the vehicle's model, e.g. F-150 9949 * @param {number} year the vehicle's model year, e.g. 2020 9950 * @param {string} stockType 'New' or 'Used' 9951 * @param {string} color the vehicle's exterior color 9952 * @param {string} transmission the vehicle's transmission type, e.g. Automatic 9953 * @param {string} bodyStyle the vehicle's body style, 'Van' or 'Truck' 9954 * @param {string} fuelType the vehicle's fuel type, 'Gas' or 'Diesel' 9955 * @param {string} drivetrain the vehicle's drivetrain, e.g. 4x2 9956 * @param {number} price the vehicle's 'final price' after discounts at the time of view 9957 * @param {string} trim the vehicle's trim package 9958 * @param {number} mileage the vehicle's mileage 9959 * @param {string} vin the vehicle's vin number 9960 * @param {string} fuelType the vehicle's fuel type 9961 * @return {VehicleViewData} an instance of `VehicleViewData` 9962 */ 9963 function VehicleViewData( 9964 inventoryId, 9965 dealerNumericId, 9966 zipCode, 9967 make, 9968 model, 9969 year, 9970 stockType, 9971 color, 9972 transmission, 9973 bodyStyle, 9974 fuelType, 9975 drivetrain, 9976 price, 9977 trim, 9978 mileage, 9979 vin, 9980 fuelType 9981 ) { 9982 this.inventoryId = inventoryId; 9983 this.dealerNumericId = dealerNumericId; 9984 this.zipCode = zipCode; 9985 this.make = make; 9986 this.model = model;
9987 this.year = year; 9988 this.stockType = stockType; 9989 this.color = color; 9990 this.transmission = transmission; 9991 this.bodyStyle = bodyStyle; 9992 this.fuelType = fuelType; 9993 this.drivetrain = drivetrain; 9994 this.price = price; 9995 this.trim = trim; 9996 this.mileage = mileage; 9997 this.vin = vin; 9998 this.fuelType = fuelType; 9999 } 10000 10001 // Singleton marketing events object 10002 modules.MarketingEvents = new MarketingEvents(new modules.EventEmitter()); 10003 10004 // constructors for tracking data objects 10005 modules.VehicleLeadData = VehicleLeadData; 10006 modules.VehicleViewData = VehicleViewData; 10007 10008})(window.wts,window.wts.modules);; 10009(function (wts, modules) { 10010 10011 10012 /** 10013 * @class FacebookMarketingEvents 10014 * @param {MarketingEvents} marketingEvents a `MarketingEvents` object 10015 * @returns {void} 10016 */ 10017 function FacebookPixel(marketingEvents) { 10018 if (typeof marketingEvents === "undefined") { 10019 throw "MarketingEvents dependency not found"; 10020 } 10021 10022 this.marketingEvents = marketingEvents; 10023 10024 return this; 10025 } 10026 10027 FacebookPixel.prototype = { 10028 10029 /** 10030 * Initializes facebook pixel with pixelId 10031 * @param {string} pixelId The dealer's facebook pixel id 10032 * @returns {void} 10033 */ 10034 init: function (pixelId) { 10035 /** 10036 * Facebook Pixel injection script from: https:// 10037 * */ 10038 (function (f, b, e, v, n, t, s) { 10039 if (f.fbq) { 10040 return; 10041 } n = f.fbq = function () { 10042 n.callMethod ? 10043 n.callMethod.apply(n, arguments) : n.queue.push(arguments); 10044 }; 10045 if (!f._fbq) { 10046 f._fbq = n; 10047 } n.push = n; n.loaded = !0; n.version = "2.0"; 10048 n.queue = []; t = b.createElement(e); t.async = !0; 10049 t.src = v; s = b.getElementsByTagName(e)[0]; 10050 s.parentNode.insertBefore(t, s); 10051 })(window, document, "script", 10052 "https://connect.facebook.net/en_US/fbevents.js"); 10053 10054 this.pixel = window.fbq; 10055 10056 this.marketingEvents.onVehicleView(this.vehicleViewHandler, this); 10057 this.marketingEvents.onVehicleLead(this.vehicleLeadHandler, this); 10058 10059 this.pixel("init", pixelId); 10060 this.pixel("track", "PageView"); 10061 }, 10062 10063 /** 10064 * Sends a pixel 'lead' event to facebook 10065 * @param {Object} eventArgs the event arguments, @see EventEmitter.dispatchEvent 10066 * @returns {void} 10067 * @throws {string} If `eventArgs.data` is not a `VehicleLeadData` 10068 */ 10069 vehicleLeadHandler: function (eventArgs) { 10070 if (!(eventArgs.data instanceof modules.VehicleLeadData)) { 10071 throw "eventArgs.data must be a `VehicleLeadData`, see MarketingEvents module"; 10072 } 10073 10074 var vehicleLeadData = eventArgs.data; 10075 var that = this; 10076 10077 this.marketingEvents.getVehicleData(vehicleLeadData.inventoryId, function (vehicleViewData) { 10078 var facebookPixelVehicleData = that.mapVehicleData(vehicleViewData); 10079 10080 // Track the lead, yea! 10081 that.pixel("track", "Lead", facebookPixelVehicleData); 10082 }); 10083 }, 10084 10085 /** 10086 * Composes the event object for the viewed vehicle and calls the Facebook Pixel 'ViewContent' event 10087 * @param {Object} eventArgs the event arguments, @see EventEmitter.dispatchEvent 10088 * @return {void} 10089 * @throws {string} If `eventArgs.data` is not a `VehicleLeadData` 10090 */ 10091 vehicleViewHandler: function (eventArgs) { 10092 if (!(eventArgs.data instanceof modules.VehicleViewData)) { 10093 throw "eventArgs.data must be a `VehicleViewData`, see MarketingEvents module"; 10094 } 10095 10096 var facebookPixelVehicleData = this.mapVehicleData(eventArgs.data); 10097 10098 this.pixel("track", "ViewContent", facebookPixelVehicleData); 10099 }, 10100 10101 /** 10102 * Maps vehicle data from the event to facebook pixel's schema 10103 * @param {VehicleViewData} vehicleViewData the `VehicleViewData` to map 10104 * @returns {Object} the mapped FacebookPixelVehicleData 10105 */ 10106 mapVehicleData: function (vehicleViewData) { 10107 var drivetrain = ""; 10108 10109 switch (vehicleViewData.drivetrain) { 10110 case "4x2": drivetrain = "RWD"; break; 10111 case "4x4": drivetrain = "FOUR_WD"; break; 10112 default: drivetrain = "TWO_WD"; break; 10113 } 10114 10115 // See: https://developers.facebook.com/docs/marketing-api/dynamic-ads-auto/auto-event/ 10116 var facebookPixelVehicleData = { 10117 content_type: "vehicle", // REQUIRED: DO NOT change this, must be set to 'vehicle' 10118 content_ids: [vehicleViewData.dealerNumericId], // REQUIRED: array of vehicle IDs 10119 postal_code: vehicleViewData.zipCode, // RECOMMENDED 10120 country: "United States", // RECOMMENDED don't use abbreviations 10121 make: vehicleViewData.make, // RECOMMENDED 10122 model: vehicleViewData.model, // RECOMMENDED 10123 year: vehicleViewData.year, // RECOMMENDED 10124 state_of_vehicle: vehicleViewData.stockType, // RECOMMENDED 10125 exterior_color: vehicleViewData.color, // RECOMMENDED 10126 transmission: vehicleViewData.transmission, // RECOMMENDED 10127 body_style: vehicleViewData.bodyStyle, // RECOMMENDED 10128 fuel_type: vehicleViewData.fuelType, // RECOMMENDED 10129 drivetrain: drivetrain, // RECOMMDENDED 10130 price: vehicleViewData.price, // RECOMMENDED, up to 2 decimals optional 10131 currency: "USD", // REQUIRED if price and preferred_price_range is used 10132 // preferred_price_range: '[10000,20000]' // Optional up to two decimals, min,max 10133 trim: vehicleViewData.trim, 10134 "mileage.value": vehicleViewData.mileage, 10135 "mileage.unit": "MI", 10136 vin: vehicleViewData.vin, 10137 value: 50 10138 }; 10139 10140 return facebookPixelVehicleData; 10141 } 10142 }; 10143 10144 modules.FacebookPixel = new FacebookPixel(modules.MarketingEvents); 10145 10146})(window.wts, window.wts.modules);; 10147(function (wts, modules) { 10148 10149 /** 10150 * @class GoogleAdwordsEvents 10151 * @param {MarketingEvents} marketingEvents a `MarketingEvents` object 10152 * @returns {void} 10153 */ 10154 function GoogleAdwords(marketingEvents) { 10155 if (typeof marketingEvents === "undefined") { 10156 throw "MarketingEvents dependency not found"; 10157 } 10158 10159 this.marketingEvents = marketingEvents; 10160 10161 return this; 10162 } 10163 10164 GoogleAdwords.prototype = { 10165 10166 /** 10167 * Init initializes google adwords with the adwords id 10168 * @param {string} awId The dealer's adwords id 10169 * @returns {void} 10170 */ 10171 init: function (awId, awConversionCode) { 10172 10173 this.marketingEvents.onVehicleView(this.vehicleViewHandler, this); 10174 this.marketingEvents.onVehicleLead(this.vehicleLeadHandler, this); 10175 10176 // Google defined pieces 10177 // the processing on this is 10178 window.dataLayer = window.dataLayer || []; 10179 this.gtag = function () { 10180 window.dataLayer.push(arguments); 10181 }; 10182 10183 this.adWordsId = awId; 10184 this.gtag("js", new Date()); 10185 this.gtag("config", awId); 10186 this.awConversionCode = awConversionCode 10187 }, 10188 10189 /** 10190 * Sends a 'purchase' event to google remarketing when a lead is generated 10191 * Also sends a 'conversion' event for google adwords conversion tracking 10192 * @param {Object} eventArgs the event arguments, @see EventEmitter.dispatchEvent 10193 * @returns {void}
10194 * @throws {string} If `eventArgs.data` is not a `VehicleLeadData` 10195 */ 10196 vehicleLeadHandler: function (eventArgs) { 10197 if (!(eventArgs.data instanceof modules.VehicleLeadData)) { 10198 throw "eventArgs.data must be a `VehicleLeadData`, see MarketingEvents module"; 10199 } 10200 10201 var vehicleLeadData = eventArgs.data; 10202 var that = this; 10203 10204 this.marketingEvents.getVehicleData(vehicleLeadData.inventoryId, function (vehicleViewData) { 10205 // Track the lead, yea! 10206 var adwordsData = that.mapVehicleData(vehicleViewData); 10207 10208 // Google adwords remarketing event 10209 that.gtag("event", "purchase", adwordsData); 10210 10211 // Google adwords conversion tracking event 10212 that.gtag("event", "conversion", { "send_to": that.adWordsId + "/" + that.awConversionCode, "event_callback": function () { } }); 10213 }); 10214 // Comvoy Conversion Code = "/ElITCLvnhroBEJjDjOQC" 10215 }, 10216 10217 /** 10218 * Composes the event object for the viewed vehicle and calls the Google AdWords remarketing event event 10219 * @param {Object} eventArgs the event arguments, @see EventEmitter.dispatchEvent 10220 * @return {void} 10221 * @throws {string} If `eventArgs.data` is not a `VehicleLeadData` 10222 */ 10223 vehicleViewHandler: function (eventArgs) { 10224 if (!(eventArgs.data instanceof modules.VehicleViewData)) { 10225 throw "eventArgs.data must be a `VehicleViewData`, see MarketingEvents module"; 10226 } 10227 10228 var adwordsData = this.mapVehicleData(eventArgs.data); 10229 10230 this.gtag("event", "view_item", adwordsData); 10231 }, 10232 10233 /** 10234 * Maps from `VehicleViewData` to google's remarketing object 10235 * @param {VehicleViewData} vehicleViewData the `VehicleViewData` to map 10236 * @returns {any} the google adwords remarketing object 10237 */ 10238 mapVehicleData: function (vehicleViewData) { 10239 // See: https://support.google.com/google-ads/answer/7305793 10240 var adwordsData = { 10241 value: vehicleViewData.price, 10242 items: [{ 10243 id: vehicleViewData.dealerNumericId, 10244 // location_id: TBD - could use the dealer ID here 10245 google_business_vertical: "custom" 10246 }] 10247 }; 10248 10249 return adwordsData; 10250 } 10251 }; 10252 10253 modules.GoogleAdwords = new GoogleAdwords(modules.MarketingEvents); 10254 10255})(window.wts, window.wts.modules);; 10256(function (wts, modules) { 10257 10258 /** 10259 * @class BingUET 10260 * @param {MarketingEvents} marketingEvents a `MarketingEvents` object 10261 * @returns {void} 10262 */ 10263 function BingUET(marketingEvents) { 10264 if (typeof marketingEvents === "undefined") { 10265 throw "MarketingEvents dependency not found"; 10266 } 10267 10268 this.marketingEvents = marketingEvents; 10269 10270 return this; 10271 } 10272 10273 BingUET.prototype = { 10274 10275 /** 10276 * Init initializes bing uet 10277 * @param {string} uetTagId The Universal Event Tracking tag id 10278 * @returns {void} 10279 */ 10280 init: function (uetTagId) { 10281 10282 var that = this; 10283 10284 var bing = function (w, d, t, r, u, id) { 10285 var f, n, i; that[u] = w[u] = w[u] || [], 10286 f = function () { 10287 var o = { ti: id }; 10288 o.q = w[u], 10289 w[u] = new UET(o), 10290 w[u].push("pageLoad"), 10291 that[u] = w[u]; 10292 }, 10293 n = d.createElement(t), 10294 n.src = r, 10295 n.async = 1, 10296 n.onload = n.onreadystatechange = function () { 10297 var s = this.readyState; 10298 s && s !== "loaded" && s !== "complete" || (f(), n.onload = n.onreadystatechange = null); 10299 }, 10300 i = d.getElementsByTagName(t)[0], 10301 i.parentNode.insertBefore(n, i); 10302 }; 10303 10304 bing(window, document, "script", "//bat.bing.com/bat.js", "uetq", uetTagId); 10305 10306 // Connect marketing events 10307 this.marketingEvents.onVehicleView(this.vehicleViewHandler, this); 10308 this.marketingEvents.onVehicleLead(this.vehicleLeadHandler, this); 10309 }, 10310 10311 /** 10312 * Sends a Bing Custom Event 10313 * @param {Object} eventArgs the event arguments, @see EventEmitter.dispatchEvent 10314 * @returns {void}
10315 * @throws {string} If `eventArgs.data` is not a `VehicleLeadData` 10316 */ 10317 vehicleLeadHandler: function (eventArgs) { 10318 if (!(eventArgs.data instanceof modules.VehicleLeadData)) { 10319 throw "eventArgs.data must be a `VehicleLeadData`, see MarketingEvents module"; 10320 } 10321 10322 var vehicleLeadData = eventArgs.data; 10323 var that = this; 10324 10325 this.marketingEvents.getVehicleData(vehicleLeadData.inventoryId, function (vehicleViewData) { 10326 10327 var bingData = that.mapVehicleData(vehicleViewData); 10328 10329 bingData.ecomm_pagetype = "purchase"; 10330 10331 var vehicleData = Object.assign({ "event_category": "Remarketing"}, bingData); 10332 10333 that.uetq.push("event", "generated_lead", vehicleData); 10334 }); 10335 }, 10336 10337 /** 10338 * Composes the event object for the viewed vehicle and calls the Bing UET Custom Event 10339 * @param {Object} eventArgs the event arguments, @see EventEmitter.dispatchEvent 10340 * @return {void} 10341 * @throws {string} If `eventArgs.data` is not a `VehicleLeadData` 10342 */ 10343 vehicleViewHandler: function (eventArgs) { 10344 if (!(eventArgs.data instanceof modules.VehicleViewData)) { 10345 throw "eventArgs.data must be a `VehicleViewData`, see MarketingEvents module"; 10346 } 10347 10348 var bingData = this.mapVehicleData(eventArgs.data); 10349 10350 bingData.ecomm_pagetype = "product"; 10351 10352 var vehicleData = Object.assign({ "event_category": "Remarketing"}, bingData); 10353 10354 this.uetq.push("event", "view_item", vehicleData); 10355 }, 10356 10357 /** 10358 * Maps from `VehicleViewData` to google's remarketing object 10359 * @param {VehicleViewData} vehicleViewData the `VehicleViewData` to map 10360 * @returns {any} the bing remarketing object 10361 */ 10362 mapVehicleData: function (vehicleViewData) { 10363 // See: https://support.google.com/google-ads/answer/7305793 10364 var bingData = { 10365 "content_type": "vehicle", 10366 "ecomm_prodid": [vehicleViewData.dealerNumericId] 10367 }; 10368 10369 return bingData; 10370 } 10371 }; 10372 10373 modules.BingUET = new BingUET(modules.MarketingEvents); 10374 10375})(window.wts, window.wts.modules);;
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.