1/** 2 * NOTICE OF LICENSE 3 * 4 * This source file is subject to a trade license awared by 5 * Garamo Online L.T.D. 6 * 7 * Any use, reproduction, modification or distribution 8 * of this source file without the written consent of 9 * Garamo Online L.T.D It Is prohibited. 10 * 11 * @author ReactionCode <[email protected]> 12 * @copyright 2015-2018 Garamo Online L.T.D 13 * @license Commercial license 14 */ 15 16function RcAnalyticsEvents() { 17 18 // reference to this 19 var publicValues = this; 20 21 //////////////////////////////////// 22 // Private values 23 24 var privateValues = {}; 25 26 // get module url from common js var prestashop 27 privateValues.moduleUrl = baseDir + 'modules/rcpganalytics/'; 28 29 // don't change this value!! used for redirect after hit 30 privateValues.redirected = false; 31 privateValues.redirectLink = null; 32 33 // products position detected on scroll tracking 34 privateValues.productsPosition = {}; 35 36 // scroll tracking event 37 privateValues.initial = true; 38 privateValues.sendProducts = []; 39 privateValues.sentProducts = []; 40 privateValues.sendPromotions = []; 41 privateValues.sentPromotions = []; 42 privateValues.scrollTimeout = null; 43 44 // product page 45 privateValues.lastIdProductView = ''; 46 47 // don not track 48 privateValues.doNotTrack = ( 49 window.doNotTrack == "1" || 50 navigator.doNotTrack == "yes" || 51 navigator.doNotTrack == "1" || 52 navigator.msDoNotTrack == "1" 53 ); 54 55 //////////////////////////////////// 56 // Public values 57 58 // all tracking features 59 publicValues.trackingFeatures = null; 60 61 publicValues.lists = null; 62 63 // cache products 64 publicValues.productsListCache = null; 65 66 // remarketing page type 67 publicValues.ecommPageType = 'other'; 68 69 // checkout 70 publicValues.shippingEventName = ''; 71 publicValues.paymentEventName = ''; 72 publicValues.opcEventName = ''; 73 publicValues.pageStep = 1; 74 75 // Theme Events 76 publicValues.eventScrollList = eventScrollList; 77 publicValues.eventClickPromotionItem = eventClickPromotionItem; 78 publicValues.eventClickProductList = eventClickProductList; 79 publicValues.eventAddCartProductList = eventAddCartProductList; 80 publicValues.eventProductView = eventProductView; 81 publicValues.eventAddCartProductView = eventAddCartProductView; 82 publicValues.eventSocialShareProductView = eventSocialShareProductView; 83 publicValues.eventWishListProductView = eventWishListProductView; 84 publicValues.eventRemoveFromCart = eventRemoveFromCart; 85 publicValues.eventCartQuantityUp = eventCartQuantityUp; 86 publicValues.eventCartQuantityDown = eventCartQuantityDown; 87 publicValues.eventCartQuantityDelete = eventCartQuantityDelete; 88 publicValues.eventCheckoutStepThree = eventCheckoutStepThree; 89 publicValues.eventCheckoutStepFour = eventCheckoutStepFour; 90 publicValues.eventOpcSignUpPrestashop = eventOpcSignUpPrestashop; 91 publicValues.eventOpcPrestashop = eventOpcPrestashop; 92 publicValues.eventOpcZelarg = eventOpcZelarg; 93 publicValues.eventOpcBestKit = eventOpcBestKit; 94 publicValues.eventOpcPrestaTeam = eventOpcPrestaTeam; 95 publicValues.eventOpcSuperCheckout = eventOpcSuperCheckout; 96 97 // Tracking Methods 98 publicValues.onSearchResults = onSearchResults; 99 publicValues.onCheckoutProducts = onCheckoutProducts; 100 publicValues.onAddOrder = onAddOrder; 101 publicValues.onSignUp = onSignUp; 102 103 // GTAG Methods 104 publicValues.sendGtagConfig = sendGtagConfig; 105 106 // Common Methods 107 publicValues.setClientId = setClientIdInDb; 108 109 // Singleton Pattern 110 if (RcAnalyticsEvents.prototype.getInstance) { 111 return RcAnalyticsEvents.prototype.getInstance; 112 } 113 114 RcAnalyticsEvents.prototype.getInstance = this; 115 /////////////////////////////////////////////// 116 117 /////////////////////////////////////////////// 118 // TEMPLATE EVENTS 119 120 // PRODUCT LISTS - Scroll 121 function eventScrollList() { 122 if (!privateValues.initial) { 123 clearTimeout(privateValues.scrollTimeout); 124 scrollElementDetection(); 125 126 privateValues.scrollTimeout = setTimeout(function() { 127 if (privateValues.sendProducts.length || privateValues.sendPromotions.length) { 128 doneScroll(); 129 } 130 }, 800); 131 } else { 132 privateValues.initial = false;
133 scrollElementDetection(); 134 doneScroll(); 135 } 136 } 137 138 // PROMOTION CLICK - Click on promotion 139 function eventClickPromotionItem(event) { 140 var mainSelector = ['.js-ga-track-promo a']; 141 var target = delegateEvents(mainSelector, event.target); 142 var promoQuery; 143 var promoLink; 144 145 // Check if GA is blocked by uBlock or similar 146 if ( 147 event.button === 0 && 148 target && 149 target.nodeName === 'A' && 150 window.ga && 151 window.ga.length 152 ) { 153 promoQuery = target.search; 154 promoLink = target.href; 155 156 if (promoQuery && promoLink) { 157 event.preventDefault(); 158 onPromotionClick(promoQuery, promoLink); 159 } 160 161 } 162 163 } 164 165 // PRODUCT LISTS - Click on product 166 function eventClickProductList(event) { 167 var mainSelector = ['.ajax_block_product']; 168 var variantSelector = ['.color-list-container a']; 169 var eventSelectors = [ 170 '.ajax_block_product .product_img_link', 171 '.ajax_block_product .product-name', 172 '.ajax_block_product .quick-view', 173 '.ajax_block_product .quick-view-mobile', 174 '.ajax_block_product .lnk_view', 175 '.ajax_block_product .color_pick', 176 '.ajax_block_product .new-box', 177 '.ajax_block_product .sale-box' 178 ]; 179 var target = delegateEvents(eventSelectors, event.target); 180 var caseClick = 1; 181 var classList; 182 var link; 183 var productNode; 184 var variantNode; 185 var variantAttribute; 186 var idProduct; 187 var idProductAttribute; 188 var list; 189 190 // Check if Google analytics is blocked by uBlock or similar 191 if (event.button === 0 && target && target.nodeName === 'A' && window.ga && window.ga.length) { 192 // if click done with ctrl or shift key avoid preventDefault 193 if (!event.ctrlKey && !event.shiftKey) { 194 // get the target class list 195 classList = target.classList; 196 197 // If Quick view event don't get link redirection 198 if (!classList.contains('quick-view') && !classList.contains('quick-view-mobile')) { 199 // retrieve the product link. 200 link = target.getAttribute('href'); 201 202 if (link) { 203 // prevent redirection on normal click 204 event.preventDefault(); 205 } 206 } 207 } 208 209 // Get the product node 210 productNode = delegateEvents(mainSelector, target); 211 212 // Get variant node 213 variantNode = delegateEvents(variantSelector, target); 214 215 if (productNode) { 216 idProduct = parseInt(productNode.getAttribute('data-id-product')); 217 idProductAttribute = parseInt(productNode.getAttribute('data-id-product-attribute')); 218 } 219 220 // Check if any filter is applied 221 list = checkFilters(); 222 223 if (!isNaN(idProduct)) { 224 // If selected color variant 225 if (variantNode) { 226 // get the attribute selected 227 variantAttribute = variantNode.getAttribute('data-id-product-attribute'); 228 229 if (variantAttribute) { 230 // if exist update the id product attribute 231 idProductAttribute = variantAttribute; 232 } 233 } 234 235 // check if idProductAttribute has valid value 236 if (isNaN(idProductAttribute)) { 237 idProductAttribute = 0; 238 } 239 240 // add the attribute to idProduct 241 idProduct = idProduct + '-' + idProductAttribute; 242 243 // Send data to GA without link redirection 244 getData(caseClick, idProduct, list, link, null); 245 } else if (link) { 246 //If idProduct not detected redirect to product page 247 document.location = link; 248 } 249 } 250 } 251 252 // PRODUCT LISTS - Add to cart 253 function eventAddCartProductList(event) { 254 var mainSelector = ['.ajax_block_product']; 255 var eventSelectors = ['.ajax_add_to_cart_button']; 256 var target = delegateEvents(eventSelectors, event.target); 257 var caseClick = 2; 258 var link; 259 var productNode; 260 var idProduct; 261 var idProductAttribute; 262 var list; 263 264 if (event.button === 0 && target && target.nodeName === 'A' && window.ga && window.ga.length) { 265 // if click done with ctrl or shift key avoid preventDefault 266 if (!event.ctrlKey && !event.shiftKey) { 267 // retrieve the product link. 268 link = target.getAttribute('href'); 269 270 if (link) { 271 // prevent redirection on normal click 272 event.preventDefault(); 273 } 274 } 275 276 productNode = delegateEvents(mainSelector, target); 277 278 if (productNode) { 279 idProduct = parseInt(productNode.getAttribute('data-id-product')); 280 idProductAttribute = parseInt(productNode.getAttribute('data-id-product-attribute')); 281 } 282 283 // Check if any filter is applied 284 list = checkFilters(); 285 286 if (!isNaN(idProduct)) { 287 // check if idProductAttribute has valid value 288 if (isNaN(idProductAttribute)) { 289 idProductAttribute = 0; 290 } 291 292 // add the attribute to idProduct 293 idProduct = idProduct + '-' + idProductAttribute; 294 295 // Send data to GA 296 getData(caseClick, idProduct, list, link, 1); 297 298 } else { 299 // if cartAjax disabled redirect to checkout 300 if (link && !publicValues.trackingFeatures.cartAjax) { 301 document.location = link; 302 } 303 } 304 } 305 } 306 307 // PRODUCT VIEW - View 308 function eventProductView(event) { 309 var target = document.body; 310 var productViewList = publicValues.lists.productView; 311 var idProductNode; 312 var idProductAttributeNode; 313 var idProductValue; 314 var idProductAttributeValue; 315 var idProductView; 316 317 // set time out to be sure that executes after product updated 318 setTimeout(function(){ 319 // retrieve document.body related to event to make compatible with iframe 320 if (event) {
321 // check if event comes from mouse or Content Loaded 322 if (event.type === 'click') { 323 target = event.view.window.document.body 324 } else { 325 // event from content loaded 326 target = event.target.body; 327 } 328 } 329 idProductNode = target.querySelector('#product_page_product_id'); 330 idProductValue = idProductNode.value; 331 idProductAttributeNode = target.querySelector('#idCombination'); 332 idProductAttributeValue = idProductAttributeNode.value || '0'; 333 334 // normalize id product to track 335 idProductView = idProductValue + '-' + idProductAttributeValue; 336 337 if (idProductView !== privateValues.lastIdProductView) { 338 // check if is a quick-view window 339 if (document.body.id !== 'product') { 340 productViewList = 'quick_view'; 341 } 342 343 getData(5, idProductView, productViewList, null, null); 344 privateValues.lastIdProductView = idProductView; 345 } 346 }); 347 } 348 349 // PRODUCT VIEW - Add to cart 350 function eventAddCartProductView(event) { 351 var eventSelectors = ['#add_to_cart input', '#add_to_cart button']; 352 var target = delegateEvents(eventSelectors, event.target); 353 var caseClick = 2; 354 var productViewList = publicValues.lists.productView; 355 var productForm; 356 var idProductNode; 357 var idProductAttributeNode; 358 var quantityWantedNode; 359 var idProduct; 360 var idProductAttribute; 361 var quantityWanted; 362 363 if (event.button === 0 && target && window.ga && window.ga.length) { 364 // gets the buy block from product view or quick view 365 productForm = event.currentTarget.querySelector('#buy_block'); 366 367 if (productForm) { 368 idProductNode = productForm.querySelector('#product_page_product_id'); 369 idProductAttributeNode = productForm.querySelector('#idCombination'); 370 quantityWantedNode = productForm.querySelector('#quantity_wanted'); 371 372 idProduct = parseInt((idProductNode ? idProductNode.value : null)); 373 idProductAttribute = parseInt((idProductAttributeNode ? idProductAttributeNode.value : null)); 374 quantityWanted = parseInt((quantityWantedNode ? quantityWantedNode.value : '1')); 375 376 if (!isNaN(idProduct)) { 377 // check if is a quick-view window 378 if (document.body.id !== 'product') { 379 productViewList = 'quick_view'; 380 } 381 382 // check if idProductAttribute has valid value 383 if (isNaN(idProductAttribute)) { 384 idProductAttribute = 0; 385 } 386 387 // add the attribute to idProduct 388 idProduct = idProduct + '-' + idProductAttribute; 389 390 getData(caseClick, idProduct, productViewList, null, quantityWanted); 391 } 392 } 393 } 394 } 395 396 // PRODUCT VIEW - Social actions 397 function eventSocialShareProductView(event) { 398 var eventSelectors = ['#send_friend_button', '.social-sharing']; 399 var target = delegateEvents(eventSelectors, event.target); 400 var network = 'email'; 401 402 if (event.button === 0 && target && window.ga && window.ga.length) { 403 // check target is social sharing module 404 if (target.classList.contains('social-sharing')) { 405 network = target.getAttribute('data-type'); 406 } 407 408 // check network value to avoid bad template 409 // customizations on social sharing module 410 if (network) { 411 onSocialAction(network); 412 } 413 } 414 } 415 416 // PRODUCT VIEW - Wish list 417 function eventWishListProductView(event) { 418 var eventSelectors = ['#wishlist_button_nopop', '#wishlist_button']; 419 var target = delegateEvents(eventSelectors, event.target); 420 var caseClick = 4; 421 var productViewList = publicValues.lists.productView; 422 var productForm; 423 var idProductNode; 424 var idProductAttributeNode; 425 var idProduct; 426 var idProductAttribute; 427 428 if (event.button === 0 && target && window.ga && window.ga.length) { 429 // gets the buy block from product view or quick view 430 productForm = event.currentTarget.querySelector('#buy_block'); 431 432 if (productForm) { 433 idProductNode = productForm.querySelector('#product_page_product_id'); 434 idProductAttributeNode = productForm.querySelector('#idCombination'); 435 idProduct = parseInt((idProductNode ? idProductNode.value : null)); 436 idProductAttribute = parseInt((idProductAttributeNode ? idProductAttributeNode.value : null)); 437 438 if (!isNaN(idProduct)) { 439 // check if is a quick-view window 440 if (document.body.id !== 'product') { 441 productViewList = 'quick_view'; 442 } 443 444 // check if idProductAttribute has valid value 445 if (isNaN(idProductAttribute)) { 446 idProductAttribute = 0; 447 } 448 449 // add the attribute to idProduct 450 idProduct = idProduct + '-' + idProductAttribute; 451 452 getData(caseClick, idProduct, productViewList, null, null); 453 } 454 } 455 } 456 } 457 458 // BLOCKCART - REMOVE PRODUCT 459 function eventRemoveFromCart(event) { 460 var eventSelectors = ['.ajax_cart_block_remove_link']; 461 var cartProductSelectors = ['[data-id^=cart_block_product_]']; 462 var cartCustomizableSelectors = ['[data-id^=deleteCustomizableProduct_]']; 463 var target = delegateEvents(eventSelectors, event.target); 464 var caseClick = 3; 465 var customizationId = 0; 466 var idProduct = 0; 467 var idProductAttribute = 0; 468 var quantityRemoved = 1; 469 var list = publicValues.lists.default; 470 var firstCut; 471 var productNode; 472 var customizableProductNode; 473 var link; 474 var ids; 475 476 if (event.button === 0 && target && target.nodeName === 'A' && window.ga && window.ga.length) { 477 // if click done with ctrl or shift key avoid preventDefault 478 if (!event.ctrlKey && !event.shiftKey) { 479 // retrieve the product link. 480 link = target.getAttribute('href'); 481 482 if (link) { 483 // prevent redirection on normal click 484 event.preventDefault(); 485 } 486 } 487 488 customizableProductNode = delegateEvents(cartCustomizableSelectors, target); 489 490 // customizable product case 491 if (customizableProductNode) { 492 493 firstCut = customizableProductNode.getAttribute('data-id'); 494 ids = firstCut.split('_'); 495 496 if (typeof(ids[1]) !== 'undefined') { 497 498 customizationId = parseInt(ids[1]); 499 idProduct = parseInt(ids[2]); 500 501 if (typeof(ids[3]) !== 'undefined') { 502 idProductAttribute = parseInt(ids[3]); 503 } 504 } 505 // todo: get the quantity removed on customized product 506 } 507 508 // Common product management 509 if (!customizationId) { 510 511 productNode = delegateEvents(cartProductSelectors, target); 512 513 quantityRemoved = parseInt(productNode.querySelector('.quantity').textContent.trim()); 514 515 //retrieve idProduct and idProductAttribute from the displayed product in the block cart 516 firstCut = productNode.getAttribute('data-id'); 517 firstCut = firstCut.replace('cart_block_product_', ''); 518 firstCut = firstCut.replace('deleteCustomizableProduct_', ''); 519 520 ids = firstCut.split('_'); 521 522 idProduct = parseInt(ids[0]); 523 524 if (typeof(ids[1]) !== 'undefined') { 525 idProductAttribute = parseInt(ids[1]); 526 } 527 } 528 529 if (!isNaN(idProduct) && !isNaN(quantityRemoved)) { 530 // check if idProductAttribute has valid value 531 if (isNaN(idProductAttribute)) { 532 idProductAttribute = 0; 533 } 534 535 // add the attribute to idProduct 536 idProduct = idProduct + '-' + idProductAttribute; 537 538 if (document.body.id === 'product') { 539 list = publicValues.lists.productView; 540 } 541 542 // Get product Data and send to GA 543 getData(caseClick, idProduct, list, link, quantityRemoved); 544 } else { 545 // if cartAjax disabled redirect to checkout 546 if (link && !publicValues.trackingFeatures.cartAjax) { 547 document.location = link; 548 } 549 } 550 } 551 } 552 553 // SUMMARY CART - Decrease product 554 function eventCartQuantityDown(event) { 555 var eventSelectors = ['.cart_quantity_down']; 556 var target = delegateEvents(eventSelectors, event.target); 557 var caseClick = 3; 558 var quantityRemoved = 1; 559 var firstCut; 560 var ids; 561 var idProduct; 562 var idProductAttribute; 563 564 if (event.button === 0 && target && window.ga && window.ga.length) { 565 firstCut = target.getAttribute('id'); 566 ids = firstCut.split('_'); 567 568 idProduct = parseInt(ids[3]); 569 570 if (typeof(ids[4]) !== 'undefined') { 571 idProductAttribute = parseInt(ids[4]); 572 } 573 574 if (!isNaN(idProduct)) { 575 // check if idProductAttribute has valid value 576 if (isNaN(idProductAttribute)) { 577 idProductAttribute = 0; 578 } 579 580 // add the attribute to idProduct 581 idProduct = idProduct + '-' + idProductAttribute; 582 583 getData(caseClick, idProduct, publicValues.lists.default, null, quantityRemoved); 584 } 585 } 586 } 587 588 // SUMMARY CART - Increase product 589 function eventCartQuantityUp(event) { 590 var eventSelectors = ['.cart_quantity_up']; 591 var target = delegateEvents(eventSelectors, event.target); 592 var caseClick = 2; 593 var quantityWanted = 1; 594 var firstCut; 595 var ids; 596 var idProduct; 597 var idProductAttribute; 598 599 if (event.button === 0 && target && window.ga && window.ga.length) { 600 firstCut = target.getAttribute('id'); 601 ids = firstCut.split('_'); 602 603 idProduct = parseInt(ids[3]); 604 605 if (typeof(ids[4]) !== 'undefined') { 606 idProductAttribute = parseInt(ids[4]); 607 } 608 609 if (!isNaN(idProduct)) { 610 // check if idProductAttribute has valid value 611 if (isNaN(idProductAttribute)) { 612 idProductAttribute = 0; 613 } 614 615 // add the attribute to idProduct 616 idProduct = idProduct + '-' + idProductAttribute; 617 618 getData(caseClick, idProduct, publicValues.lists.default, null, quantityWanted); 619 } 620 } 621 } 622 623 // SUMMARY CART - Remove product 624 function eventCartQuantityDelete(event) { 625 var eventSelectors = ['.cart_quantity_delete']; 626 var mainNode = ['[id^=product_]']; 627 var target = delegateEvents(eventSelectors, event.target); 628 var caseClick = 3; 629 var firstCut; 630 var ids; 631 var idProduct; 632 var idProductAttribute; 633 var quantityRemoved; 634 635 if (event.button === 0 && target && window.ga && window.ga.length) { 636 firstCut = target.getAttribute('id'); 637 ids = firstCut.split('_'); 638 639 idProduct = parseInt(ids[0]); 640 641 if (typeof(ids[1]) !== 'undefined') { 642 idProductAttribute = parseInt(ids[1]); 643 } 644 645 quantityRemoved = delegateEvents(mainNode, target); 646 647 if (quantityRemoved) { 648 quantityRemoved = quantityRemoved.querySelector('.cart_quantity input[type=hidden]'); 649 quantityRemoved = parseInt((quantityRemoved ? quantityRemoved.value : null)); 650 } 651 652 if (!isNaN(idProduct) && !isNaN(quantityRemoved)) { 653 // check if idProductAttribute has valid value 654 if (isNaN(idProductAttribute)) { 655 idProductAttribute = 0; 656 } 657 658 // add the attribute to idProduct 659 idProduct = idProduct + '-' + idProductAttribute; 660 661 // send data to GA 662 getData(caseClick, idProduct, publicValues.lists.default, null, quantityRemoved); 663 } 664 } 665 } 666 667 // CHECKOUT - STEP 3 - Carrier selection 668 function eventCheckoutStepThree(){ 669 var cgv = document.querySelector('#cgv'); 670 var mainCarrierSelector = ['.delivery_option']; 671 672 var checkoutValue = ''; 673 674 var shippingNode; 675 676 if (!cgv || cgv.checked) { 677 // get shipping node 678 shippingNode = document.querySelector('.delivery_option_radio:checked'); 679 680 // if virtual product don't has any shipping node 681 if (shippingNode) { 682 shippingNode = delegateEvents(mainCarrierSelector, shippingNode); 683 shippingNode = shippingNode.querySelector('tr'); 684 685 shippingNode = shippingNode.children[2]; 686 687 // get shipping text 688 checkoutValue = (shippingNode ? shippingNode.textContent.trim() : ''); 689 checkoutValue = normalizeText(checkoutValue); 690 } 691 692 onCheckoutOption(publicValues.shippingEventName, checkoutValue, null); 693 } 694 } 695 696 // CHECKOUT - STEP 4 - Payment selection 697 function eventCheckoutStepFour(event){ 698 var eventSelectors = ['#HOOK_PAYMENT a', '#HOOK_PAYMENT input']; 699 var advancedPayment = document.querySelector('#HOOK_ADVANCED_PAYMENT'); 700 701 var cgv; 702 var target; 703 var checkoutValue; 704 var link; 705 706 if (!advancedPayment) { 707 target = delegateEvents(eventSelectors, event.target); 708 } else { 709 target = document.querySelector('#HOOK_ADVANCED_PAYMENT .payment_selected'); 710 } 711 712 if (event.button === 0 && target && window.ga && window.ga.length) { 713 cgv = document.querySelector('#cgv'); 714 715 if (!cgv || cgv.checked) { 716 // if click done with ctrl or shift key avoid preventDefault 717 if (!event.ctrlKey && !event.shiftKey) { 718 // get link for classic payments 719 link = (target.getAttribute('onclick') ? null : target.getAttribute('href')); 720 721 if (link) { 722 // prevent redirection on normal click 723 event.preventDefault(); 724 } 725 } 726 727 // selected payment 728 checkoutValue = target.getAttribute('title') || target.textContent.trim(); 729 checkoutValue = normalizeText(checkoutValue); 730 731 onCheckoutOption(publicValues.paymentEventName, checkoutValue, link); 732 } 733 } 734 } 735 736 // CHECKOUT - OCP - Customer signup 737 function eventOpcSignUpPrestashop(event){ 738 var eventSelectors = ['#submitAccount', '#submitGuestAccount']; 739 var target = delegateEvents(eventSelectors, event.target); 740 741 if (event.button === 0 && target) { 742 getSignUpGoal(); 743 } 744 } 745 746 // CHECKOUT - OPC - Options selected 747 function eventOpcPrestashop(event){ 748 var eventSelectors = ['#HOOK_PAYMENT a', '#HOOK_PAYMENT input']; 749 var mainCarrierSelector = ['.delivery_option']; 750 var advancedPayment = document.querySelector('#HOOK_ADVANCED_PAYMENT'); 751 var shippingOption = ''; 752 753 var target; 754 var cgv; 755 var shippingNode; 756 var paymentMethod; 757 var checkoutValue; 758 var link; 759 760 if (!advancedPayment) { 761 target = delegateE
761vents(eventSelectors, event.target); 762 } else { 763 target = document.querySelector('#HOOK_ADVANCED_PAYMENT .payment_selected'); 764 } 765 766 if (event.button === 0 && target && window.ga && window.ga.length) { 767 cgv = document.querySelector('#cgv'); 768 769 if (!cgv || cgv.checked) { 770 // if click done with ctrl or shift key avoid preventDefault 771 if (!event.ctrlKey && !event.shiftKey) { 772 // get link for classic payments 773 link = (target.getAttribute('onclick') ? null : target.getAttribute('href')); 774 775 if (link) { 776 // prevent redirection on normal click 777 event.preventDefault(); 778 } 779 } 780 781 // get shipping node 782 shippingNode = document.querySelector('.delivery_option_radio:checked'); 783 784 // if virtual product don't has any shipping node 785 if (shippingNode) { 786 shippingNode = delegateEvents(mainCarrierSelector, shippingNode); 787 shippingNode = shippingNode.querySelector('tr'); 788 shippingNode = shippingNode.children[2]; 789 790 // get shipping text 791 shippingOption = (shippingNode ? shippingNode.textContent.trim() : ''); 792 shippingOption = normalizeText(shippingOption); 793 } 794 795 // get selected payment 796 paymentMethod = target.getAttribute('title') || target.textContent.trim(); 797 paymentMethod = normalizeText(paymentMethod); 798 799 checkoutValue = paymentMethod + ' / ' + shippingOption; 800 801 onCheckoutOption(publicValues.opcEventName, checkoutValue, link); 802 } 803 } 804 } 805 806 // CHECKOUT - OPC - Options selected 807 function eventOpcZelarg(event){ 808 809 var eventSelectors = ['.confirm_button', '#HOOK_PAYMENT a', '#HOOK_PAYMENT input']; 810 var mainCarrierSelector = ['tr']; 811 var mainPaymentSelector = ['tr']; 812 var target = delegateEvents(eventSelectors, event.target); 813 814 var shippingOption = ''; 815 var link = null; 816 817 var cgv; 818 var shippingMode; 819 var shippingNode; 820 var paymentMode; 821 var paymentNode; 822 var paymentMethod; 823 var checkoutValue; 824 825 if (event.button === 0 && target && window.ga && window.ga.length) { 826 cgv = document.querySelector('#cgv'); 827 828 if (!cgv || cgv.checked) { 829 shippingMode = document.querySelector('.carrier_name'); 830 paymentMode = document.querySelector('#HOOK_PAYMENT_PARSED'); 831 832 if (shippingMode) { 833 // shipping panel as Zelarg style 834 835 // get selected shipping node 836 shippingNode = document.querySelector('.carrier_action input:checked'); 837 838 // if virtual product don't has any shipping node 839 if (shippingNode) { 840 shippingNode = delegateEvents(mainCarrierSelector, shippingNode); 841 shippingNode = shippingNode.querySelector('.carrier_name img') || shippingNode.querySelector('.carrier_name label'); 842 843 // get selected shipping option 844 shippingOption = shippingNode.getAttribute('alt') || shippingNode.textContent.trim(); 845 } 846 } else { 847 // shipping panel as PS style 848 849 // get shipping node 850 shippingNode = document.querySelector('.delivery_option_radio:checked'); 851 852 if (shippingNode) { 853 shippingNode = delegateEvents(mainCarrierSelector, shippingNode); 854 shippingNode = shippingNode.querySelector('tr'); 855 shippingNode = shippingNode.children[2]; 856 857 // get shipping text 858 shippingOption = (shippingNode ? shippingNode.textContent.trim() : ''); 859 } 860 } 861 862 if (paymentMode) { 863 // payment panel as Zelarg style 864 865 // get selected payment node 866 paymentNode = document.querySelector('.payment_action input:checked'); 867 paymentNode = delegateEvents(mainPaymentSelector, paymentNode); 868 paymentNode = paymentNode.querySelector('.payment_description label'); 869 870 // get selected payment option 871 paymentMethod = (paymentNode ? paymentNode.textContent.trim() : ''); 872 } else { 873 // payment panel as PS style 874 link = (target.getAttribute('onclick') ? null : target.getAttribute('href')); 875 876 // get selected payment 877 paymentMethod = target.getAttribute('title') || target.textContent.trim(); 878 paymentMethod = normalizeText(paymentMethod); 879 } 880 881 // clean text 882 shippingOption = normalizeText(shippingOption); 883 paymentMethod = normalizeText(paymentMethod); 884 885 // concatenate options 886 checkoutValue = paymentMethod + ' / ' + shippingOption; 887 888 // if goal enabled check the customer sign up 889 if (publicValues.trackingFeatures.goals.signUp) { 890 getSignUpGoal(); 891 } 892 893 onCheckoutOption(publicValues.opcEventName, checkoutValue, link); 894 } 895 } 896 } 897 898 // CHECKOUT - OPC - Options selected 899 function eventOpcBestKit(event){ 900 901 var eventSelectors = ['#HOOK_PAYMENT a', '#HOOK_PAYMENT input']; 902 var mainCarrierSelector = ['.shipping-delivery-item-opc']; 903 var target = delegateEvents(eventSelectors, event.target); 904 905 var shippingOption = ''; 906 var link = null; 907 908 var cgv; 909 var shippingNode; 910 var paymentMethod; 911 var checkoutValue; 912 913 if (event.button === 0 && target && window.ga && window.ga.length) { 914 cgv = document.querySelector('#cgv'); 915 916 if (!cgv || cgv.checked) { 917 // if click done with ctrl or shift key avoid preventDefault 918 if (!event.ctrlKey && !event.shiftKey) { 919 // get link for classic payments 920 link = (target.getAttribute('onclick') ? null : target.getAttribute('href')); 921 922 if (link) { 923 // prevent redirection on normal click 924 event.preventDefault(); 925 } 926 } 927 928 // get shipping node 929 shippingNode = document.querySelector('.delivery_option_radio:checked'); 930 931 // if virtual product don't has any shipping node 932 if (shippingNode) { 933 shippingNode = delegateEvents(mainCarrierSelector, shippingNode); 934 shippingNode = shippingNode.querySelector('.shipping-title-opc'); 935 936 // get shipping text 937 shippingOption = (shippingNode ? shippingNode.textContent.trim() : ''); 938 shippingOption = normalizeText(shippingOption); 939 } 940 941 // get selected payment 942 paymentMethod = target.getAttribute('title') || target.textContent.trim(); 943 paymentMethod = normalizeText(paymentMethod); 944 945 checkoutValue = paymentMethod + ' / ' + shippingOption; 946 947 // if goal enabled check the customer sign up 948 if (publicValues.trackingFeatures.goals.signUp) { 949 getSignUpGoal(); 950 } 951 952 onCheckoutOption(publicValues.opcEventName, checkoutValue, link); 953 } 954 } 955 } 956 957 // CHECKOUT - OPC - Options selected 958 function eventOpcPrestaTeam(event){ 959 var eventSelectors = ['#btn_place_order']; 960 var mainCarrierSelector = ['.delivery_option']; 961 var mainPaymentSelector = ['.module_payment_container']; 962 var target = delegateEvents(eventSelectors, event.target); 963 964 var shippingOption = ''; 965 966 var cgv; 967 var shippingNode; 968 var paymentNode; 969 var paymentMethod; 970 var checkoutValue; 971 972 if (event.button === 0 && target && window.ga && window.ga.length) { 973 cgv = document.querySelector('#cgv'); 974 975 if (!cgv || cgv.checked) { 976 // get selected shipping node 977 shippingNode = document.querySelector('.delivery_option input:checked'); 978 979 // if virtual product don't has any shipping node 980 if (shippingNode) { 981 shippingNode = delegateEvents(mainCarrierSelector, shippingNode); 982 shippingNode = shippingNode.querySelector('.delivery_option_title'); 983 984 // get selected shipping option 985 shippingOption = (shippingNode ? shippingNode.textContent.trim() : ''); 986 shippingOption = normalizeText(shippingOption); 987 } 988 989 // get selected payment node 990 paymentNode = document.querySelector('.payment_input input:checked'); 991 992 if (paymentNode) { 993 // continue only if payment selected 994 paymentNode = delegateEvents(mainPaymentSelector, paymentNode); 995 paymentNode = paymentNode.querySelector('.payment_content span'); 996 997 // get selected payment option 998 paymentMethod = (paymentNode ? paymentNode.textContent.trim() : ''); 999 paymentMethod = normalizeText(paymentMethod); 1000 1001 checkoutValue = paymentMethod + ' / ' + shippingOption; 1002 1003 // if goal enabled check the customer sign up 1004 if (publicValues.trackingFeatures.goals.signUp) { 1005 getSignUpGoal(); 1006 } 1007 1008 onCheckoutOption(publicValues.opcEventName, checkoutValue, null); 1009 } 1010 } 1011 } 1012 } 1013 1014 // CHECKOUT - OPC - Options selected 1015 function eventOpcSuperCheckout(event){ 1016 var eventSelectors = ['#supercheckout_confirm_order']; 1017 var mainCarrierSelector = ['.highlight']; 1018 var mainPaymentSelector = ['.highlight']; 1019 var target = delegateEvents(eventSelectors, event.target); 1020 1021 var shippingOption = ''; 1022 1023 var cgv; 1024 var shippingNode; 1025 var paymentNode; 1026 var paymentMethod; 1027 var checkoutValue; 1028 1029 if (event.button === 0 && target && window.ga && window.ga.length) { 1030 cgv = document.querySelector('#cgv'); 1031 1032 if (!cgv || cgv.checked) { 1033 // get selected shipping node 1034 shippingNode = document.querySelector('.supercheckout_shipping_option:checked'); 1035 1036 // if virtual product don't has any shipping node 1037 if (shippingNode) { 1038 shippingNode = delegateEvents(mainCarrierSelector, shippingNode); 1039 shippingNode = shippingNode.querySelector('label img') || shippingNode.querySelector('label'); 1040 1041 // get selected shipping option 1042 shippingOption = (shippingNode ? shippingNode.getAttribute('alt') || shippingNode.textContent.trim() : ''); 1043 shippingOption = normalizeText(shippingOption); 1044 } 1045 1046 // get selected payment node 1047 paymentNode = document.querySelector('#payment-method input:checked'); 1048 paymentNode = delegateEvents(mainPaymentSelector, paymentNode); 1049 paymentNode = paymentNode.querySelector('label img') || paymentNode.querySelector('label span'); 1050 1051 // get selected payment option 1052 paymentMethod = (paymentNode ? paymentNode.getAttribute('alt') || paymentNode.textContent.trim() : ''); 1053 paymentMethod = normalizeText(paymentMethod); 1054 1055 checkoutValue = paymentMethod + ' / ' + shippingOption; 1056 1057 // if goal enabled check the customer sign up 1058 if (publicValues.trackingFeatures.goals.signUp) { 1059 // todo: this payment mod has a bad account creation, find a solution 1060 getSignUpGoal(); 1061 } 1062 1063 onCheckoutOption(publicValues.opcEventName, checkoutValue, null); 1064 } 1065 } 1066 } 1067
1068 ///////////////////////////////////////////// 1069 // TRACKING METHODS 1070 1071 // SEARCH RESULT - get the search term 1072 function onSearchResults() { 1073 var eventName = 'view_search_results'; 1074 var eventParams = {}; 1075 var searchTerm; 1076 1077 // verify that page is search 1078 if (document.body.id === 'search') { 1079 searchTerm = getSearchTerm(); 1080 if (searchTerm) { 1081 eventParams.search_term = searchTerm; 1082 sendGtagEvent(eventName, eventParams); 1083 } 1084 } 1085 } 1086 1087 // PRODUCT LISTS - scroll tracking 1088 function onScrollTracking(products) { 1089 var eventName = 'view_item_list'; 1090 var eventParams; 1091 var eventDimensions; 1092 var sendNow; 1093 1094 // check if is an array and is not empty 1095 if (Array.isArray(products) && products.length) { 1096 while (products.length > 0) { 1097 // get products to send 1098 sendNow = products.splice(0, publicValues.trackingFeatures.productSendRate); 1099 1100 // init params to avoid send duplicates 1101 eventParams = { 1102 // add non_interaction to fix bounce rates 1103 'non_interaction': 1, 1104 'items': [] 1105 }; 1106 1107 // parse all products to send 1108 sendNow.forEach(function (product) { 1109 // set product on params 1110 eventParams.items.push(productLayer(product)); 1111 }); 1112 1113 // set remarketing dimensions to params 1114 if (publicValues.trackingFeatures.config.remarketing) { 1115 eventDimensions = setRemarketingDimensions(sendNow, publicValues.ecommPageType); 1116 // add custom dimensions to params 1117 Object.assign(eventParams, eventDimensions); 1118 } 1119 1120 // send gtag event 1121 sendGtagEvent(eventName, eventParams); 1122 } 1123 } 1124 } 1125 1126 // gtm event - promo view 1127 function onPromotionView(promotions) { 1128 var eventName = 'view_promotion'; 1129 var eventParams = { 1130 'promotions': getPromotionsLayered(promotions) 1131 }; 1132 1133 // send gtag event 1134 sendGtagEvent(eventName, eventParams); 1135 } 1136 1137 // gtag event - promo click 1138 function onPromotionClick(promotion, link) { 1139 var eventName = 'select_content'; 1140 var eventParams = { 1141 'promotions': [getPromotionLayer(promotion)] 1142 }; 1143 1144 if (link) { 1145 privateValues.redirectLink = link; 1146 eventParams['event_callback'] = callbackWithTimeout( 1147 function() { 1148 redirectLink(); 1149 }, 1150 2000 1151 ); 1152 } 1153 1154 // send gtag event 1155 sendGtagEvent(eventName, eventParams); 1156 } 1157 1158 // PRODUCT LISTS - product view click 1159 function onProductClick(product, link) { 1160 var eventName = 'select_content'; 1161 var eventParams = { 1162 'content_type': 'product', 1163 'items': [] 1164 }; 1165 1166 // set product on params 1167 eventParams.items.push(productLayer(product)); 1168 1169 if (link) { 1170 // add redirect to product page. 1171 privateValues.redirectLink = link; 1172 eventParams['event_callback'] = callbackWithTimeout( 1173 function() { 1174 redirectLink(); 1175 }, 1176 2000 1177 ); 1178 } 1179 1180 // send gtag event 1181 sendGtagEvent(eventName, eventParams); 1182 } 1183 1184 // PRODUCT VIEW - view product on their product page 1185 function onProductView(product) { 1186 var eventName = 'view_item'; 1187 var eventParams = { 1188 'non_interaction': 1, 1189 'items': [] 1190 }; 1191 var ecommPageType = publicValues.ecommPageType; 1192 var eventDimensions; 1193 1194 // set product on params 1195 eventParams.items.push(productLayer(product)); 1196 1197 if (publicValues.trackingFeatures.config.remarketing) { 1198 if (product.list_name === 'quick_view') { 1199 ecommPageType = 'product'; 1200 } 1201 eventDimensions = setRemarketingDimensions([product], ecommPageType); 1202 // add custom dimensions to params
1203 Object.assign(eventParams, eventDimensions); 1204 } 1205 // send gtag event 1206 sendGtagEvent(eventName, eventParams); 1207 } 1208 1209 // PRODUCT VIEW - social network action 1210 function onSocialAction(network) { 1211 var eventName = 'share'; 1212 var eventParams = { 1213 'method' : network, 1214 'event_value' : publicValues.trackingFeatures.eventValues.socialAction 1215 }; 1216 1217 if (publicValues.trackingFeatures.goals.socialAction) { 1218 sendGtagEvent(eventName, eventParams); 1219 } 1220 } 1221 1222 // PRODUCT VIEW - wish list 1223 function onWishList(product) { 1224 var eventName = 'add_to_wishlist'; 1225 var eventParams = { 1226 'items' : [], 1227 'value' : publicValues.trackingFeatures.eventValues.wishList 1228 }; 1229 1230 // set product on params 1231 eventParams.items.push(productLayer(product)); 1232 1233 // send gtag event 1234 sendGtagEvent(eventName, eventParams); 1235 } 1236 1237 // COMMON - new customer registration 1238 function onSignUp() { 1239 // todo check how interact with AdWords 1240 var index = publicValues.trackingFeatures.isGuest; 1241 var customerType = publicValues.trackingFeatures.signUpTypes[index]; 1242 var eventName = 'sign_up'; 1243 var eventParams = { 1244 'method' : customerType, 1245 'value' : publicValues.trackingFeatures.eventValues.signUp 1246 }; 1247 1248 if (publicValues.trackingFeatures.goals.signUp && publicValues.trackingFeatures.isNewSignUp) { 1249 // send data layer 1250 sendGtagEvent(eventName, eventParams); 1251 1252 // reset values to avoid multiple sends 1253 publicValues.trackingFeatures.isNewSignUp = 0; 1254 publicValues.trackingFeatures.isGuest = 0; 1255 } 1256 } 1257 1258 // COMMON - add to cart on product click 1259 function onAddToCart(product, link) { 1260 var eventName = 'add_to_cart'; 1261 var eventParams = { 1262 'items': [] 1263 }; 1264 var eventDimensions = {}; 1265 var ecommPageType = 'cart'; 1266 1267 // set product on params 1268 eventParams.items.push(productLayer(product)); 1269 1270 // set remarketing dimensions to params 1271 if (publicValues.trackingFeatures.config.remarketing) { 1272 eventDimensions = setRemarketingDimensions([product], ecommPageType); 1273 // add custom dimensions to params 1274 Object.assign(eventParams, eventDimensions); 1275 } 1276 1277 // Send data using an event. 1278 if (!publicValues.trackingFeatures.cartAjax && link) { 1279 privateValues.redirectLink = link; 1280 eventParams['event_callback'] = callbackWithTimeout( 1281 function() { 1282 redirectLink(); 1283 }, 1284 2000 1285 ); 1286 } 1287 1288 // send gtag event 1289 sendGtagEvent(eventName, eventParams); 1290 } 1291 1292 // COMMON - remove from cart click 1293 function onRemoveFromCart(product, link) { 1294 var eventName = 'remove_from_cart'; 1295 var eventParams = { 1296 'items': [] 1297 }; 1298 1299 // set product on params 1300 eventParams.items.push(productLayer(product)); 1301 1302 // Send data using an event. 1303 if (!publicValues.trackingFeatures.cartAjax && link) { 1304 privateValues.redirectLink = link; 1305 eventParams['event_callback'] = callbackWithTimeout( 1306 function() { 1307 redirectLink(); 1308 }, 1309 2000 1310 ); 1311 } 1312 1313 // send gtag event 1314 sendGtagEvent(eventName, eventParams); 1315 } 1316 1317 // CHECKOUT - send products and actual checkout step 1318 function onCheckoutProducts(products) { 1319 var eventName = 'checkout_progress'; 1320 var eventParams; 1321 var eventDimensions; 1322 var sendNow; 1323 1324 if (publicValues.pageStep === 1) { 1325 eventName = 'begin_checkout' 1326 } 1327 1328 // check if is an array and is not empty 1329 if (Array.isArray(products) && products.length) { 1330 while (products.length > 0) { 1331 // get products to send 1332 sendNow = products.splice(0, publicValues.trackingFeatures.productSendRate); 1333 1334 // init params to avoid send duplicates
1335 eventParams = { 1336 'checkout_step': publicValues.pageStep, 1337 'items': [] 1338 }; 1339 1340 // parse all products to send 1341 sendNow.forEach(function (product) { 1342 // set product on params 1343 eventParams.items.push(productLayer(product)); 1344 }); 1345 1346 // set remarketing dimensions to params 1347 if (publicValues.trackingFeatures.config.remarketing) { 1348 eventDimensions = setRemarketingDimensions(sendNow, publicValues.ecommPageType); 1349 // add custom dimensions to params 1350 Object.assign(eventParams, eventDimensions); 1351 } 1352 1353 // send gtag event 1354 sendGtagEvent(eventName, eventParams); 1355 } 1356 } 1357 } 1358 1359 // CHECKOUT - option selected by customer 1360 function onCheckoutOption(checkoutOption, checkoutValue, link) { 1361 var eventName = 'set_checkout_option'; 1362 var eventParams = { 1363 'checkout_step': publicValues.pageStep, 1364 'checkout_option': checkoutOption, 1365 'value': checkoutValue 1366 }; 1367 1368 if (link !== null) { 1369 privateValues.redirectLink = link; 1370 eventParams['event_callback'] = callbackWithTimeout( 1371 function() { 1372 redirectLink(); 1373 }, 1374 2000 1375 ); 1376 } 1377 1378 // send gtag event 1379 sendGtagEvent(eventName, eventParams); 1380 } 1381 1382 // CHECKOUT - process order data 1383 function onAddOrder(order, products, idShop) { 1384 var eventName = 'purchase'; 1385 var adWordsConversionEventName = 'conversion'; 1386 var adWordsConversionParams = { 1387 'transaction_id': order.transaction_id, 1388 'value': order.value, 1389 'currency': publicValues.trackingFeatures.currency 1390 }; 1391 var productLength = products.length; 1392 var firstLoop = 1; 1393 var eventDimensions; 1394 var eventParams; 1395 var sendNow; 1396 1397 if (Array.isArray(products) && products.length) { 1398 while (products.length > 0) { 1399 // get products to send 1400 sendNow = products.splice(0, publicValues.trackingFeatures.productSendRate); 1401 1402 // init params to avoid send duplicates 1403 eventParams = { 1404 'items': [], 1405 'send_to': publicValues.trackingFeatures.analyticsId 1406 }; 1407 1408 // add event callback on first loop 1409 if (firstLoop) { 1410 eventParams['event_callback'] = callbackWithTimeout( 1411 function() { 1412 setOrderInDb(order.transaction_id, idShop); 1413 }, 1414 1000 1415 ); 1416 firstLoop = 0; 1417 } 1418 1419 // set order values to params 1420 Object.keys(order).forEach(function (key) { 1421 if (order[key] !== null && order[key] !== false) { 1422 // if order is spliced don't save value key 1423 if (key === 'value' && productLength > publicValues.trackingFeatures.productSendRate) { 1424 // skip value key 1425 return; 1426 } 1427 eventParams[key] = order[key]; 1428 } 1429 }); 1430 1431 // parse all products to send 1432 sendNow.forEach(function (product) { 1433 // set product on params 1434 eventParams.items.push(productLayer(product)); 1435 }); 1436 1437 // send remarketing dimensions to params 1438 if (publicValues.trackingFeatures.config.remarketing) { 1439 eventDimensions = setRemarketingDimensions(sendNow, publicValues.ecommPageType); 1440 // add custom dimensions to params 1441 Object.assign(eventParams, eventDimensions); 1442 } 1443 1444 // send gtag event
1445 sendGtagEvent(eventName, eventParams); 1446 } 1447 1448 // after process ga transaction send AdWords transaction 1449 if (publicValues.trackingFeatures.adwordsId && publicValues.trackingFeatures.adwordsCl) { 1450 // required to process adwords conversion 1451 adWordsConversionParams['send_to'] = 1452 publicValues.trackingFeatures.adwordsId + 1453 '/' + 1454 publicValues.trackingFeatures.adwordsCl 1455 ; 1456 1457 sendGtagEvent(adWordsConversionEventName, adWordsConversionParams); 1458 } 1459 } 1460 } 1461 1462 // TRACKING - Configure GTAG 1463 function sendGtagConfig(configId) { 1464 var configElement = publicValues.trackingFeatures[configId]; 1465 var configFeatures = publicValues.trackingFeatures.config; 1466 var configParams = {}; 1467 var doNotTrack = (publicValues.trackingFeatures.checkDoNotTrack && privateValues.doNotTrack); 1468 1469 if (publicValues.trackingFeatures.disableInternalTracking || doNotTrack) { 1470 window['ga-disable-' + publicValues.trackingFeatures.analyticsId] = true; 1471 } 1472 1473 // add params for analytics configuration 1474 if (configId === 'analyticsId') { 1475 // add configParams 1476 configParams['site_speed_sample_rate'] = configFeatures.simpleSpeedSampleRate; 1477 configParams['anonymize_ip'] = configFeatures.anonymizeIp; 1478 configParams['link_attribution'] = configFeatures.linkAttribution; 1479 1480 if (configFeatures.userIdFeature) { 1481 configParams['user_id'] = configFeatures.userIdValue; 1482 } 1483 1484 if (configFeatures.remarketing) { 1485 configParams['custom_map'] = {}; 1486 configParams.custom_map['dimension' + configFeatures.customDimensions['ecommProdId']] = 'ecomm_prodid'; 1487 configParams.custom_map['dimension' + configFeatures.customDimensions['ecommPageType']] = 'ecomm_pagetype'; 1488 configParams.custom_map['dimension' + configFeatures.customDimensions['ecommTotalValue']] = 'ecomm_totalvalue'; 1489 configParams.custom_map['dimension' + configFeatures.customDimensions['ecommCategory']] = 'ecomm_category'; 1490 } else { 1491 configParams['allow_display_features'] = configFeatures.remarketing; 1492 } 1493 1494 if (Array.isArray(configFeatures.crossDomainList) && configFeatures.crossDomainList.length) { 1495 configParams['linker'] = {'domains': configFeatures.crossDomainList}; 1496 } 1497 1498 if (configFeatures.optimizeId) { 1499 configParams['optimize_id'] = configFeatures.optimizeId; 1500 } 1501 1502 // set permanent values 1503 configParams['currency'] = publicValues.trackingFeatures.currency; 1504 } else if (configId === 'adwordsId') { 1505 // avoid send remarketing hit on page view to AdWords 1506 configParams['send_page_view'] = false; 1507 } 1508 1509 if (configElement) { 1510 gtag('config', configElement, configParams); 1511 } 1512 } 1513 1514 // TRACKING - Send event 1515 function sendGtagEvent(eventName, eventParams) { 1516 // send event to analytics 1517 gtag('event', eventName, eventParams); 1518 } 1519 1520 ///////////////////////////////////////////// 1521 // TRACKING TOOLS 1522 function getPromotionsLayered(promotions) { 1523 var promotionsLayered = []; 1524 1525 promotions.forEach(function (promotion) { 1526 promotionsLayered.push(getPromotionLayer(promotion)); 1527 }); 1528 1529 return promotionsLayered; 1530 } 1531 1532 function getPromotionLayer(promotion) { 1533 var promotionFields = { 1534 'pid': 'id', 1535 'pn': 'name', 1536 'pc': 'creative_name', 1537 'pp': 'creative_slot' 1538 }; 1539 var promotionLayer = {}; 1540 var promotionQueryData; 1541 var gaKey; 1542 1543 promotionQueryData = getQueryData(promotion); 1544 1545 Object.keys(promotionFields).forEach(function(key) { 1546 gaKey = promotionFields[key]; 1547 if (promotionQueryData.hasOwnProperty(key)) { 1548 promotionLayer[gaKey] = decodeURIComponent(promotionQueryData[key]); 1549 } 1550 }); 1551 return promotionLayer; 1552 } 1553 1554 // GENERAL - get product model 1555 function productLayer(product) { 1556 var productKeys = [ 1557 'id', 1558 'name', 1559 'variant', 1560 'brand', 1561 'category', 1562 'list_name', 1563 'list_position', 1564 'quantity', 1565 'price', 1566 'coupon' 1567 ]; 1568 var gaProduct = {}; 1569 1570 // populate the ga productFieldObject 1571 productKeys.forEach(function(key){
1572 if (product[key] != null) { 1573 gaProduct[key] = product[key]; 1574 } 1575 }); 1576 1577 return gaProduct; 1578 } 1579 1580 // GENERAL - get remarketing dimensions 1581 function setRemarketingDimensions(products, ecommPageType) { 1582 var remarketingDimensions = { 1583 'ecomm_prodid': [], 1584 'ecomm_pagetype': ecommPageType 1585 }; 1586 var ecomm_totalvalue = 0; 1587 var productPrice = 0; 1588 var idProduct; 1589 1590 products.forEach(function(product){ 1591 // basic id product 1592 idProduct = product.id; 1593 1594 // check if product has variant 1595 if (publicValues.trackingFeatures.merchantVariant && product.id_attribute) { 1596 // set id product with variant 1597 idProduct = product.id + publicValues.trackingFeatures.merchantVariant + product.id_attribute; 1598 } 1599 1600 // add product dimension 1601 remarketingDimensions.ecomm_prodid.push( 1602 publicValues.trackingFeatures.merchantPrefix + 1603 idProduct + 1604 publicValues.trackingFeatures.merchantSuffix 1605 ); 1606 1607 if (ecommPageType === 'cart' || 1608 (ecommPageType === 'product' && product.list_name === 'product_page') || 1609 (ecommPageType === 'product' && product.list_name === 'quick_view') || 1610 ecommPageType === 'purchase' 1611 ) { 1612 // set basic product price 1613 productPrice = product.price; 1614 1615 // check if product have quantity 1616 if (product.quantity) { 1617 productPrice = productPrice * product.quantity; 1618 } 1619 // calc total_value dimension 1620 ecomm_totalvalue = (ecomm_totalvalue + productPrice); 1621 // update remarketingDimension and cut ecomm_totalvalue to 2 decimals 1622 remarketingDimensions.ecomm_totalvalue = parseFloat(ecomm_totalvalue.toFixed(2)); 1623 } 1624 1625 // set ecomm_category only on category or product pages 1626 if ((ecommPageType === 'category' || 1627 ecommPageType === 'product') && 1628 product.category 1629 ) { 1630 remarketingDimensions.ecomm_category = product.category; 1631 } 1632 }); 1633 1634 return remarketingDimensions; 1635 } 1636 1637 // SEARCH RESULT - get the search term 1638 function getSearchTerm() { 1639 var searchWordNode; 1640 var searchTerm; 1641 1642 if (document.body.id === 'search') { 1643 searchWordNode = document.querySelector('#search_query_top'); 1644 searchTerm = searchWordNode.value || null; 1645 } 1646 return searchTerm; 1647 } 1648 1649 ///////////////////////////////////////////// 1650 // AJAX REQUEST 1651 1652 // AJAX - get Product data and send to GA 1653 function getData(caseClick, idProducts, list, link, quantityWanted) { 1654 var req = new XMLHttpRequest(); 1655 var url = privateValues.moduleUrl + 'rcpganalytics-ajax.php'; 1656 var data = { 1657 'action': 'product', 1658 'products_position': privateValues.productsPosition, 1659 'list': list, 1660 'quantity_wanted': quantityWanted, 1661 'products_list_cache': publicValues.productsListCache 1662 }; 1663 var formData; 1664 var response; 1665 var type; 1666 1667 if (typeof idProducts === 'object') { 1668 // for products lists 1669 data['id_products'] = idProducts; 1670 } else { 1671 // for product page or events 1672 data['id_products'] = [idProducts]; 1673 } 1674 1675 formData = new FormData(); 1676 formData.append('data', JSON.stringify(data)); 1677 formData.append('token', publicValues.trackingFeatures.token); 1678 1679 req.open('POST', url, true); 1680 req.onreadystatechange = function () { 1681 try { 1682 if (req.status === 200) { 1683 if (req.readyState === 4) { 1684 type = req.getResponseHeader('Content-Type'); 1685 if (type === 'application/json') { 1686 response = JSON.parse(req.responseText); 1687 if (typeof response === 'object') { 1688 if (caseClick === 0) { 1689 onScrollTracking(response); 1690 } else if (caseClick === 1) { 1691 onProductClick(response[0], link); 1692 } else if (caseClick === 2) { 1693 onAddToCart(response[0], link); 1694 } else if (caseClick === 3) { 1695 onRemoveFromCart(response[0], link); 1696 } else if (caseClick === 4) { 1697 onWishList(response[0]); 1698 } else if (caseClick === 5) { 1699 onProductView(response[0]); 1700 } 1701 } 1702 } else { 1703 throw 'response is not an JSON object'; 1704 } 1705 } 1706 } else { 1707 throw 'Unexpected XHR error'; 1708 } 1709 } catch (error) { 1710 console.warn('rcpganalytics: ' + error); 1711 if (link) { 1712 // add redirect to product page. 1713 privateValues.redirectLink = link; 1714 redirectLink(); 1715 } 1716 } 1717 }; 1718 req.send(formData); 1719 } 1720 1721 // AJAX - get some goal data 1722 function getSignUpGoal() { 1723 var req = new XMLHttpRequest(); 1724 var url = privateValues.moduleUrl + 'rcpganalytics-ajax.php'; 1725 var data = { 1726 'action' : 'signUp', 1727 'maxLapse' : publicValues.trackingFeatures.maxLapse 1728 }; 1729 var formData; 1730 var response; 1731 var type; 1732 1733 // set 1s delay to allow PS create the account 1734 setTimeout(function(){ 1735 formData = new FormData(); 1736 formData.append('data', JSON.stringify(data)); 1737 formData.append('token', publicValues.trackingFeatures.token); 1738 1739 req.open('POST', url, true); 1740 req.onreadystatechange = function () { 1741 if (req.readyState === 4 && req.status === 200) { 1742 type = req.getResponseHeader('Content-Type'); 1743 if (type === 'application/json') { 1744 response = JSON.parse(req.responseText); 1745 if (typeof response === 'object') { 1746 if (response.isNewSignUp) { 1747 // set response data to publicValues 1748 publicValues.trackingFeatures.isNewSignUp = response.isNewSignUp; 1749 publicValues.trackingFeatures.isGuest = response.isGuest; 1750 1751 // process signUp goal 1752 onSignUp(); 1753 } 1754 } 1755 } 1756 } 1757 }; 1758 req.send(formData); 1759 }, 1000 1760 ); 1761 } 1762 1763 // Ajax Call - after sent transaction to GA set order data in DB 1764 function setOrderInDb(orderId, idShop) { 1765 var req = new XMLHttpRequest(); 1766 var url = privateValues.moduleUrl + 'rcpganalytics-ajax.php'; 1767 var data = { 1768 'action': 'orderComplete', 1769 'is_order': true, 1770 'id_order': orderId, 1771 'id_shop': idShop, 1772 'id_customer': publicValues.trackingFeatures.config.userIdValue 1773 }; 1774 var adBlocker = (!window.ga || !window.ga.length); 1775 var doNotTrack = (publicValues.trackingFeatures.checkDoNotTrack && privateValues.doNotTrack); 1776 var formData; 1777 1778 // check if ga is loaded 1779 if (doNotTrack || adBlocker) { 1780 data.action = 'abortedTransaction'; 1781 data.doNotTrack = privateValues.doNotTrack; 1782 data.adBlocker = adBlocker; 1783 } 1784 1785 formData = new FormData(); 1786 formData.append('data', JSON.stringify(data)); 1787 formData.append('token', publicValues.trackingFeatures.token); 1788 1789 req.open('POST', url, true); 1790 req.send(formData); 1791 } 1792 1793 // Ajax Call - check if clientId exist and set to control DB 1794 function setClientIdInDb() { 1795 var clientId; 1796 var trackers; 1797 var req; 1798 var url; 1799 var data; 1800 var formData; 1801 1802 // fire if ga is defined 1803 if (window.ga) { 1804 ga(function() { 1805 // get all trackers 1806 trackers = ga.getAll(); 1807 // check is trackers is an Array and is not empty 1808 if (Array.isArray(trackers) && trackers.length) { 1809 // get clientId of customer 1810 clientId = trackers[0].get('clientId'); 1811 1812 if (clientId && clientId !== publicValues.trackingFeatures.clientId) { 1813 req = new XMLHttpRequest(); 1814 url = privateValues.moduleUrl + 'rcpganalytics-ajax.php'; 1815 data = { 1816 'action': 'clientId', 1817 'id_customer': publicValues.trackingFeatures.config.userIdValue, 1818 'id_shop': publicValues.trackingFeatures.idSh
1818op, 1819 'client_id': clientId 1820 }; 1821 1822 formData = new FormData(); 1823 formData.append('data', JSON.stringify(data)); 1824 formData.append('token', publicValues.trackingFeatures.token); 1825 1826 req.open('POST', url, true); 1827 // setRequestHeader breaks the formData object, don't add it 1828 req.send(formData); 1829 } 1830 } 1831 }); 1832 } 1833 } 1834 1835 ///////////////////////////////////////////// 1836 // EVENTS - TOOLS 1837 1838 // SCROLL - Detect products and promos on scroll 1839 function scrollElementDetection() { 1840 var products = document.querySelectorAll('.ajax_block_product'); 1841 var promos = document.querySelectorAll('.js-ga-track-promo'); 1842 1843 if (products.length) { 1844 processScrollElement(products, 'product'); 1845 } 1846 1847 if (promos.length) { 1848 processScrollElement(promos, 'promo'); 1849 } 1850 } 1851 1852 function processScrollElement(elements, type) { 1853 var visibleElement; 1854 var idProduct; 1855 var idProductAttribute; 1856 var isInViewport; 1857 1858 elements.forEach(function(element){ 1859 isInViewport = isElementInViewport(element); 1860 1861 if (isInViewport) { 1862 // handle product cases 1863 if (type === 'product') { 1864 // get product data 1865 idProduct = parseInt(element.getAttribute('data-id-product')); 1866 idProductAttribute = parseInt(element.getAttribute('data-id-product-attribute')) | 0; 1867 1868 if (!isNaN(idProduct)) { 1869 // set element index format 1870 visibleElement = idProduct + '-' + idProductAttribute; 1871 1872 // check that element has not sent and is not a duplicate 1873 if (privateValues.sentProducts.indexOf(visibleElement) === -1 && 1874 privateValues.sendProducts.indexOf(visibleElement) === -1) { 1875 privateValues.sendProducts.push(visibleElement); 1876 } 1877 } 1878 } 1879 1880 // handle promo cases 1881 else if (type === 'promo') { 1882 // index promotions with query selector 1883 visibleElement = element.querySelector('a').search; 1884 1885 if (visibleElement) { 1886 // check that element has not sent and is not a duplicate 1887 if (privateValues.sentPromotions.indexOf(visibleElement) === -1 && 1888 privateValues.sendPromotions.indexOf(visibleElement) === -1) { 1889 privateValues.sendPromotions.push(visibleElement); 1890 } 1891 } 1892 } 1893 } 1894 }); 1895 } 1896 1897 // SCROLL - Calc product position 1898 function scrollProductPositionDetection() { 1899 // populate productsPosition counting 1900 // every product with class .ajax_block_product 1901 var products = document.querySelectorAll('.ajax_block_product'); 1902 var actualPosition = getInitPosition(); 1903 var productKey; 1904 var idProduct; 1905 var idProductAttribute; 1906 1907 products.forEach(function(product){ 1908 idProduct = parseInt(product.getAttribute('data-id-product')); 1909 idProductAttribute = parseInt(product.getAttribute('data-id-product-attribute')); 1910 1911 if (isNaN(idProductAttribute)) { 1912 idProductAttribute = 0; 1913 } 1914 1915 if (!isNaN(idProduct)) { 1916 productKey = idProduct + '-' + idProductAttribute; 1917 1918 // check if productsPosition has the product ID as key 1919 if(!privateValues.productsPosition.hasOwnProperty(productKey)) { 1920 privateValues.productsPosition[productKey] = actualPosition; 1921 actualPosition ++; 1922 } 1923 } 1924 }); 1925 } 1926 // SCROLL - Get initial product position 1927 function getInitPosition() { 1928 var pagination; 1929 var itemsNumber; 1930 1931 pagination = document.querySelector('#pagination .active span span'); 1932 pagination = (pagination ? pagination.textContent.trim() : 1); 1933 1934 // PS16 have 2 basic versions 1935 itemsNumber = 1936 document.querySelector('#nb_page_items option:checked') || 1937 document.querySelector('#nb_item option:checked'); 1938 1939 itemsNumber = (itemsNumber ? itemsNumber.value : 1); 1940 1941 // get the first product position 1942 return (parseInt(itemsNumber) * parseInt(pagination)) - parseInt(itemsNumber) + 1; 1943 }
1944 // SCROLL - Launch event 1945 function doneScroll() { 1946 var caseClick = 0; 1947 var list; 1948 1949 // check if exists new products to send 1950 if (privateValues.sendProducts.length > 0) { 1951 // calculate products position in each scroll for possible lazy loads products 1952 scrollProductPositionDetection(); 1953 list = checkFilters(); 1954 // process data to GA 1955 getData(caseClick, privateValues.sendProducts, list, null, null); 1956 // add new products to sent list 1957 Array.prototype.push.apply(privateValues.sentProducts, privateValues.sendProducts); 1958 // reset sendProducts to avoid multiple sends 1959 privateValues.sendProducts = []; 1960 } 1961 1962 // check if exists new promotions to send 1963 if (privateValues.sendPromotions.length > 0) { 1964 // send promo view to GA 1965 onPromotionView(privateValues.sendPromotions); 1966 // add new products to sent list 1967 Array.prototype.push.apply(privateValues.sentPromotions, privateValues.sendPromotions); 1968 // reset sendPromotions to avoid multiple sends 1969 privateValues.sendPromotions = []; 1970 } 1971 clearTimeout(privateValues.scrollTimeout); 1972 } 1973 1974 function checkFilters() { 1975 var list = publicValues.lists.default; 1976 var isEnabledFilter = document.querySelector('#enabled_filters'); 1977 var pmAdvancedSearch = document.querySelector('.PM_ASResetGroup'); 1978 1979 if (isEnabledFilter || pmAdvancedSearch) { 1980 list = publicValues.lists.filter; 1981 } else if (document.body.id === 'search') { 1982 list = publicValues.lists.search; 1983 publicValues.ecommPageType = 'searchresults'; 1984 } 1985 return list; 1986 } 1987 1988 /////////////////////// 1989 // COMMON TOOLS 1990 1991 // GENERAL - redirect to new location 1992 function redirectLink() { 1993 if (!privateValues.redirected) { 1994 // set flag to avoid multiple redirection 1995 privateValues.redirected = true; 1996 window.location = privateValues.redirectLink; 1997 } 1998 } 1999 2000 // GENERAL - timeout method to avoid page blocking 2001 function callbackWithTimeout(callback, timeout) { 2002 var called = false; 2003 2004 function fn() { 2005 if (!called) { 2006 called = true; 2007 callback(); 2008 } 2009 } 2010 setTimeout(fn, timeout || 1000); 2011 2012 return fn; 2013 } 2014 2015 // parse query link to get object 2016 function getQueryData(query) { 2017 var vars = {}; 2018 query.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { 2019 vars[key] = value; 2020 }); 2021 return vars; 2022 } 2023 2024 // check if element is inside viewport 2025 function isElementInViewport(element) { 2026 var isVisible = false; 2027 var winHeight = window.innerHeight; 2028 var winOffset = window.pageYOffset; 2029 var minY = winOffset; 2030 var maxY = winOffset + winHeight; 2031 var itemTop; 2032 var itemBottom; 2033 var elHeight; 2034 var elComputedStyle; 2035 var elHeightPadding; 2036 var rect; 2037 2038 // size of inner height including padding 2039 elHeight = element.clientHeight; 2040 2041 // if elHeight === 0 means element is not visible or have display none 2042 if (elHeight) { 2043 // get computed styles to retrieve the real padding applied on css styles. 2044 elComputedStyle = getComputedStyle(element); 2045 2046 // sum the top and bottom padding to get the height padding 2047 elHeightPadding = parseInt(elComputedStyle.paddingTop) + parseInt(elComputedStyle.paddingBottom); 2048 2049 // get element rectangle 2050 rect = element.getBoundingClientRect(); 2051 2052 // calc element display position 2053 itemTop = rect.top + winOffset; 2054 itemBottom = itemTop + (elHeight - elHeightPadding); 2055 2056 // check if element is inside display 2057 isVisible = ( 2058 (itemTop >= minY && itemTop < maxY) || 2059 (itemBottom >= minY && itemBottom < maxY) 2060 ); 2061 } 2062 return isVisible; 2063 } 2064 2065 // Remove extra spaces 2066 function normalizeText(text) { 2067 var filtered = ''; 2068 2069 if (typeof text === 'string') { 2070 filtered = text.replace(/^\s+|\n+.*/g, '').trim(); 2071 } 2072 2073 return filtered; 2074 } 2075 2076 // Like JQ closest 2077 function delegateEvents(selectors, target) { 2078 var matchMode;
2079 2080 if (target) { 2081 // get available browser matches function 2082 matchMode = target.matches || target.webkitMatchesSelector || target.msMatchesSelector; 2083 2084 // get function name (general browsers || iE9) 2085 matchMode = matchMode.name || /function\s+([\w\$]+)\s*\(/.exec( matchMode.toString() ); 2086 2087 // on iE9 get the name value, empty value on anonymous fn 2088 if (typeof matchMode !== 'string') { 2089 matchMode = matchMode ? matchMode[1] : ''; 2090 } 2091 2092 // continue only if we get matches selector function 2093 if (matchMode) { 2094 while (target.parentNode !== null) { 2095 if (target.nodeType === 1) { 2096 // iterate all selectors 2097 for (var i = 0; i < selectors.length; i++) { 2098 // compare if node match with selector 2099 if (target[matchMode](selectors[i])) { 2100 // if match return target 2101 return target; 2102 } 2103 } 2104 } 2105 // if no match or nodeType !== 1 go to parent 2106 target = target.parentNode; 2107 } 2108 } 2109 } 2110 } 2111}
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.