PageSourceSearch

https://www.strahlgeraeteshop.de/modules/rcpganalytics/views/js/hook/RcAnalyticsEvents.js

js strahlgeraeteshop.de collected 2026-09-28 09:28:14 UTC 77,406 bytes, 2,111 lines download raw bytes

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.