509318396
­­­­­­­­­­­­­­­­­­
<!DOCTYPE html>
<html>
<?php
namespace PixelYourSite;

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}


class AjaxHookEventManager {

    public static $DIV_ID_FOR_AJAX_EVENTS = "pys_ajax_events";
    private static $_instance;

    static function addPendingEvent($name,$event) {
        if ( ! function_exists( 'WC' ) || ! WC()->session ) return;

        $events = WC()->session->get( 'pys_events', array() );
        $events[$name] = $event;
        WC()->session->set( 'pys_events', $events );
        WC()->session->save_data();
    }

    /**
     * @param $name
     * @param $slug
     * @return mixed|null
     */
    static function getPendingEvent($name,$unset) {
        if ( function_exists( 'WC' ) ) {
            if(!WC()->session) return null;
            $session_data = WC()->session->get_session_data();
            $events = isset( $session_data['pys_events'] ) ? WC()->session->get( 'pys_events', array() ) : array();
            if (isset($events[$name])) {
                $event = $events[$name];
                if ($unset) {
                    unset($events[$name]);
                    WC()->session->set('pys_events', $events);
                    WC()->session->save_data();
                }
                return $event;
            }
            return null;
        }
        return null;
    }

    public static function instance() {

        if ( is_null( self::$_instance ) ) {
            self::$_instance = new self();
        }

        return self::$_instance;

    }

    public function __construct() {

    }

    public function addHooks() {


        if(EventsWoo()->isEnabled()) {

            // use for fb server only because ajax request cause bugs in woo


            if ( PYS()->getOption('woo_add_to_cart_on_button_click')
                && PYS()->getOption('woo_add_to_cart_enabled')
                && isEventEnabled('woo_add_to_cart_enabled')
            )
            {
                if(PYS()->getOption('woo_add_to_cart_catch_method') == "add_cart_hook") {
                    add_action( 'wp_footer', array( __CLASS__, 'addDivForAjaxPixelEvent')  );
                    add_action( 'woocommerce_add_to_cart',array(__CLASS__, 'trackWooAddToCartEvent'),40, 6);
                    if (wp_doing_ajax()) {
                        add_filter('woocommerce_add_to_cart_fragments', array(__CLASS__, 'addPixelCodeToAddToCartFragment'));
                    } else {
                        add_action("wp_footer",array(__CLASS__, 'printEvent'));
                    }
                } else {
                    add_action( 'woocommerce_after_add_to_cart_button', 'PixelYourSite\EventsManager::setupWooSingleProductData' );
                }


            }
        }



    }


    static function trackWooAddToCartEvent($cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data) {

        if(isset($cart_item_data['woosb_parent_id'])) return; // fix for WPC Product Bundles for WooCommerce (Premium) product

        $is_ajax_request = wp_doing_ajax();
        if( isset( $_REQUEST['action'] ) && $_REQUEST['action'] === 'yith_wacp_add_item_cart') {
            $is_ajax_request = true;
        }

        $standardParams = getStandardParams( $product_id );

        PYS()->getLog()->debug('trackWooAddToCartEvent is_hook_request '.$is_ajax_request);
        $dataList = [];
        // Generate ONE shared eventID for the entire add-to-cart action so that
        // all pixels receive the same ID for server-side deduplication.
        $sharedEventId = EventIdGenerator::guidv4();
        foreach ( PYS()->getRegisteredPixels() as $pixel ) {

			if ( !Consent()->mayFire( $pixel->getSlug() ) ) {
				continue;
			}

            if( !empty($variation_id)
                && $variation_id > 0
                && (($pixel->getSlug() === 'ga' && !GATags()->getOption( 'woo_variable_as_simple')) ||
                    ($pixel->getSlug() === "facebook" && !Facebook\Helpers\facebookVariableAsSimple() ) ||
                    (!in_array($pixel->getSlug(), ['ga', 'facebook']) && !$pixel->getOption( 'woo_variable_as_simple' ))
                )
            ) {
                $_product_id = $variation_id;
            } else {
                $_product_id = $product_id;
            }


            // $_product_id drives the content/item id (parent when "treat variable as simple" is on).
            // priceId always points at the selected variation so the item price reflects what the
            // customer actually picked, instead of the cheapest variation of the parent.
            $price_id = ( ! empty( $variation_id ) && $variation_id > 0 ) ? $variation_id : $product_id;

            $event = new SingleEvent('woo_add_to_cart_on_button_click',EventTypes::$STATIC,'woo');
            $event->args = ['productId' => $_product_id,'priceId' => $price_id,'quantity' => $quantity];
            $event->addPayload(['eventID' => $sharedEventId]);
            $events = $pixel->generateEvents( $event );

            if ( empty($events) ) {
                continue; // event is disabled or not supported for the pixel
            }
            $event = $events[0];

            // add standard params
	        $slug = $pixel->getSlug();
	        if( $slug !== "reddit" ) {
		        $event->addParams($standardParams);
	        }

            // prepare event data
            $eventData = $event->getData();
            $eventData = EventsManager::filterEventParams($eventData,"woo",['event_id'=>$event->getId(),'pixel'=>$pixel->getSlug()]);

            $dataList[$pixel->getSlug()] = $eventData;
            if(!PYS()->is_user_agent_bot()){
                if($pixel->getSlug() === "facebook" && Facebook()->isServerApiEnabled()) {
                    FacebookServer()->sendEventsNow([$event]);
                }

                if($pixel->getSlug() === "pinterest" && Pinterest()->isServerApiEnabled()) {
                    PinterestServer()->sendEventsNow(array($event));
                }

                if($pixel->getSlug() === "openai" && OpenAI()->isServerApiEnabled()) {
                    OpenAIServer()->sendEventsNow(array($event));
                }
            }

        }
        AjaxHookEventManager::addPendingEvent("woo_add_to_cart_on_button_click",$dataList);
    }

    public static function printEvent() {
        $pixelsEventData = self::getPendingEvent("woo_add_to_cart_on_button_click",true);
        if( !empty($pixelsEventData) ) {
            PYS()->getLog()->debug('trackWooAddToCartEvent printEvent is footer');
            echo "<div id='pys_late_event' style='display:none' dir='".json_encode($pixelsEventData,JSON_HEX_APOS)."'></div>";
        }
    }

    public  static function addDivForAjaxPixelEvent(){

        echo self::getDivForAjaxPixelEvent();
        ?>
        <script>
            var node = document.getElementsByClassName('woocommerce-message')[0];
            if(node && document.getElementById('pys_late_event')) {
                var messageText = node.textContent.trim();
                if(!messageText) {
                    node.style.display = 'none';
                }
            }
        </script>
        <?php
    }

    public  static function getDivForAjaxPixelEvent($content = ''){
        return "<div id='".self::$DIV_ID_FOR_AJAX_EVENTS."'>" . $content . "</div>";
    }

    public static function addPixelCodeToAddToCarMessage($message, $products, $show_qty) {
        $pixelsEventData = self::getPendingEvent("woo_add_to_cart_on_button_click",true);
        if( !empty($pixelsEventData) ){
            $message .= "<div id='pys_late_event' dir='".json_encode($pixelsEventData,JSON_HEX_APOS)."'></div>";
        }
        return $message;
    }

    public static function addPixelCodeToAddToCartFragment($fragments) {


        $pixelsEventData = self::getPendingEvent("woo_add_to_cart_on_button_click",true);
        if( !empty($pixelsEventData) ){
            PYS()->getLog()->debug('addPixelCodeToAddToCartFragment send data with fragment');
            $pixel_code = self::generatePixelCode($pixelsEventData);
            $fragments['#'.self::$DIV_ID_FOR_AJAX_EVENTS] =
                self::getDivForAjaxPixelEvent($pixel_code);
        }

        return $fragments;
    }

    public static function generatePixelCode($pixelsEventData){

        // Extract the shared eventID and unique e_id keys from PHP event data (set once
        // in trackWooAddToCartEvent before the pixel loop, so all pixels carry the same UUID).
        $sharedEventId = '';
        $uniqueEIds    = [];
        $events        = [];
        foreach ($pixelsEventData as $slug => $eventData) {
            if (empty($sharedEventId) && !empty($eventData['eventID'])) {
                $sharedEventId = $eventData['eventID'];
            }
            $eId = !empty($eventData['custom_event_post_id'])
                    ? $eventData['custom_event_post_id']
                    : ($eventData['e_id'] ?? '');
            if (!empty($eId) && !in_array($eId, $uniqueEIds, true)) {
                $uniqueEIds[] = $eId;
            }
            $events[] = ['slug' => $slug, 'event' => $eventData];
        }

        ob_start();
        ?>
        <script>
            // Runs on every cart-fragments refresh, possibly before public.js (held
            // back by a consent tool or a script optimizer): keep everything local
            // and wait for PYS instead of calling into it blindly.
            (function () {
                var cookieName = 'pysAddToCartFragmentId';
                var cartHash = <?= wp_json_encode( isWooCartAvailable() ? WC()->cart->get_cart_hash() : '' ) ?>;
                var uniqueEIds = <?= wp_json_encode( $uniqueEIds ) ?>;
                var events = <?= wp_json_encode( $events ) ?>;
                var attempts = 0;

                function getCookie(name) {
                    var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
                    return v ? v[2] : null;
                }

                function token() {
                    if (window.crypto && typeof window.crypto.randomUUID === 'function') {
                        return window.crypto.randomUUID();
                    }
                    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                        var r = Math.random() * 16 | 0;
                        return (c === 'x' ? r : (r % 4 + 8)).toString(16);
                    });
                }

                function fire() {
                    if (!window.pys || typeof window.pys.getPixelBySlag !== 'function') {
                        if (++attempts < 50) {
                            setTimeout(fire, 200);
                        }
                        return;
                    }
                    if (getCookie(cookieName) == cartHash) { // prevent re send event if user update page
                        return;
                    }

                    // Use the PHP-generated shared eventID; fall back to a JS token if absent.
                    var eventId = <?= wp_json_encode( (string) $sharedEventId ) ?> || token();

                    // Sync the uniqueId cache once per event type so generateUniqueId()
                    // returns the same fresh ID even when ajaxForServerStaticEvent=true.
                    if (typeof window.pys.setEventUniqueId === 'function') {
                        uniqueEIds.forEach(function (eId) {
                            window.pys.setEventUniqueId(eId, eventId);
                        });
                    }

                    events.forEach(function (item) {
                        var pixel = window.pys.getPixelBySlag(item.slug);
                        if (!pixel) {
                            return;
                        }
                        item.event.eventID = eventId;
                        pixel.fireEvent(item.event.name, item.event);
                    });

                    // Woo re-applies cached fragments on page load: the cookie stops a re-send.
                    var d = new Date();
                    d.setTime(d.getTime() + 24 * 60 * 60 * 1000 * 90);
                    document.cookie = cookieName + "=" + cartHash + ";path=/;expires=" + d.toGMTString();
                }

                fire();
            })();
        </script>
        <?php

        $code = ob_get_clean();
        return $code;
    }




}
