<?php

/**
 * Resource builder class. Compiles the plugin generating generated javascript
 * and CSS output. Distributed between below and above the fold (depends on plugin optimization settings).
 *
 * The resource load is certified by the WP Rocket <https://wp-rocket.me> to ensure that all cache and resources
 * are working properly on all sites and uses the latest technologies.
 *
 * @author appscreo
 * @package EasySocialShareButtons
 * @since 3.0
 * @version 5.0
 * @modified 7.3
 */

class ESSB_Plugin_Assets
{

    /**
     * Control the version strings
     */
    public $resource_version = ESSB3_VERSION;

    /**
     * Load all scripts in the head instead of footer
     */
    public $scripts_in_head = false;

    /**
     * Include the dynamic styles into the site footer (for optimization purpose)
     */
    public $inline_css_footer = false;

    /**
     * Load JavaScript files as async
     * @var bool
     */
    public $js_async = false;

    /**
     * Load JavaScript files as deferred
     * @var bool
     */
    public $js_defer = false;

    /**
     * Load JavaScript files in the head
     * @var bool
     */
    public $js_head = false;

    /**
     * Inline JavaScript code output in the head
     * @var array
     */
    public $js_code_head = array();

    /**
     * Inline JavaScript code output in the footer
     * @var array
     */
    public $js_code = array();

    /**
     * Inline JavaScript code that should not be cached
     * @var array
     */
    public $js_code_noncachable = array();

    /**
     * Static JavaScript file URLs (async capable)
     * @var array
     */
    public $js_static = array();

    /**
     * Static JavaScript file URLs that should not be loaded async
     * @var array
     */
    public $js_static_nonasync = array();

    /**
     * Static JavaScript file URLs enqueued for the footer
     * @var array
     */
    public $js_static_footer = array();

    /**
     * Static JavaScript file URLs enqueued for the footer without async
     * @var array
     */
    public $js_static_noasync_footer = array();

    /**
     * Social API keys that are already loaded
     * @var array
     */
    public $js_social_apis = array();

    /**
     * CSS queue for precompiled resource mode
     * @var array
     */
    public $precompiled_css_queue = array();

    /**
     * JS queue for precompiled resource mode
     * @var array
     */
    public $precompiled_js_queue = array();

    /**
     * Active resource keys tracker
     * @var array
     */
    public $active_resources = array();

    /**
     * Collected inline styles for core style attachment
     * @var string
     */
    private $inline_styles = '';

    /**
     * Precompiled mode active for CSS
     * @var bool
     */
    private $precompile_css = false;

    /**
     * Precompiled mode active for JavaScript
     * @var bool
     */
    private $precompile_js = false;

    /**
     * Precompiled CSS has been loaded flag
     * @var bool
     */
    private $precompile_css_loaded = false;

    /**
     * Plugin is deactivated on the current page
     * @var bool
     */
    private $plugin_deactivate = false;

    /**
     * Load precompiled resources in the footer
     * @var bool
     */
    private $precompiled_footer = false;

    /**
     * Add preloading option for the plugin styles
     * @var bool
     */
    private $precompile_css_preload = false;

    /**
     * Precompiled CSS filename for preloading
     * @var string
     */
    private $precompile_css_filename = '';

    /**
     * Singleton instance
     * @var self|null
     */
    private static $instance = null;

    /**
     * Prevent multiple class instances
     */
    public static function instance()
    {

        if (null == self::$instance) {
            self::$instance = new self;
        }

        return self::$instance;
    }


    /**
     * Initialize the resource builder, set up hooks and options
     */
    public function __construct()
    {
        add_filter('body_class', array($this, 'flag_body_class'));

        $this->inline_css_footer = essb_option_bool_value('load_css_footer');

        /**
         * Determine the state of the pre-compiled mode. Pre-compiled mode collects all styles
         * and scripts and load them below the fold. Action is the same that cache plugins
         * does to optimize page load.
         */
        $precompiled_mode = false;
        $precompiled_mode_css = false;
        $precompiled_mode_js = false;

        if (defined('ESSB3_PRECOMPILED_RESOURCE')) {
            $precompiled_mode_resources = essb_option_value('precompiled_mode');

            if ($precompiled_mode_resources == '' || $precompiled_mode_resources == 'css') {
                $precompiled_mode_css = true;
            }
            if ($precompiled_mode_resources == '' || $precompiled_mode_resources == 'js') {
                $precompiled_mode_js = true;
            }

            if ($precompiled_mode_css) {
                $this->inline_css_footer = true;
            }

            $precompiled_mode = true;

            $this->precompile_css = $precompiled_mode_css;
            $this->precompile_js = $precompiled_mode_js;

            $this->precompiled_footer = essb_option_bool_value('precompiled_footer');
            $this->precompile_css_preload = essb_option_bool_value('precompiled_preload_css');
        }

        /**
         * Generating the dynamic CSS code
         */
        if (!$precompiled_mode_css) {
            if ($this->inline_css_footer) {
                add_action('essb_rs_footer', array($this, 'generate_custom_css'), 997);
            } else {
                add_action('essb_rs_head_enqueue', array($this, 'generate_custom_css'));
            }
            add_action('essb_rs_footer', array($this, 'generate_custom_footer_css'), 998);
        } else {
            add_action('essb_rs_footer', array($this, 'generate_custom_css_precompiled'), 996);
        }

        /**
         * Generate scripts
         */
        add_action('essb_rs_head', array($this, 'generate_custom_js'));

        if (!$precompiled_mode_js) {
            add_action('essb_rs_footer', array($this, 'generate_custom_footer_js'), 998);
        } else {
            add_action('essb_rs_footer', array($this, 'generte_custom_js_precompiled'), 996);
        }

        // initalize resource builder options based on settings
        $this->js_head = essb_option_bool_value('scripts_in_head');
        $this->js_async = essb_option_bool_value('load_js_async');
        $this->js_defer = essb_option_bool_value('load_js_defer');

        // static CSS and javascripts sources enqueue
        add_action('wp_enqueue_scripts', array($this, 'check_optimized_load'), 1);
        add_action('wp_enqueue_scripts', array($this, 'register_front_assets'), 10);

        // load pre-compiled mode cache if exist
        if ($precompiled_mode_css) {
            add_action('wp_enqueue_scripts', array($this, 'register_precompile_styles'), 11);
        }

        /**
         * @since 10.1 Priority loading assets (Javascript and CSS). The priority loaded assets are not cached.
         */
        add_action('wp_head', array($this, 'header_priority_load'), 9);

        add_action('wp_head', array($this, 'header'));
        add_action('wp_footer', array($this, 'footer'), 999);

        if (essb_option_bool_value('remove_ver_resource')) {
            $this->resource_version = '';
        }
    }

    /**
     * Add extra body classes based on plugin settings
     *
     * @param array $classes
     * @return array
     */
    public function flag_body_class($classes)
    {
        $classes[] = 'essb-' . ESSB3_VERSION;

        return $classes;
    }

    /**
     * Check if reCAPTCHA script needs to be loaded
     * 
     * @return bool
     */
    public function should_load_recaptcha()
    {
        $recaptcha = essb_option_bool_value('mail_recaptcha') && ! empty(essb_sanitize_option_value('mail_recaptcha_site')) && ! empty(essb_sanitize_option_value('mail_recaptcha_secret'));

        if ($recaptcha && essb_sanitize_option_value('mail_function') == 'form') {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Additional check if the settings are made to load resources only posts where plugin is running
     * 
     * @return void
     */
    public function check_optimized_load()
    {
        if ($this->is_optimized_deactivated()) {
            $this->deactivate_actions();
        }

        $this->plugin_deactivate = essb_is_plugin_deactivated_on();

        /**
         * AMP Bind
         */
        if (class_exists('ESSBAmpSupport')) {
            if (function_exists('amp_is_request') && amp_is_request()) {
                $this->deactivate_actions();
                $this->plugin_deactivate = true;
            }
        }
    }

    /**
     * Is plugin deactivated on the current loading content
     *
     * @return boolean
     */
    public function is_optimized_deactivated()
    {
        $r = false;

        if (essb_option_value('optimize_load') == 'selected') {
            $active_types = essb_option_value('display_in_types');
            $active_type = get_post_type();

            if ($active_type == '') {
                $active_type = 'post';
            }
            $active_types = (array) $active_types;

            if (!in_array($active_type, $active_types)) {
                $r = true;
            }

            // deactivating plugin resources on list pages if the option is not activated
            if (is_archive() || is_front_page() || is_search() || is_tag() || is_post_type_archive() || is_home()) {
                if (!in_array('all_lists', $active_types)) {
                    $r = true;
                }
            }

            /**
             * Frontpage check
             */
            if (is_front_page() && !in_array('homepage', $active_types)) {
                $r = true;
            }
        } else if (essb_option_value('optimize_load') == 'post') {
            $optimize_load_id = essb_sanitize_option_value('optimize_load_id');
            if ($optimize_load_id != '') {
                $excule_from = explode(',', $optimize_load_id);

                $excule_from = array_map('trim', $excule_from);
                if (!in_array(get_the_ID(), $excule_from, false)) {
                    $r = true;
                }
            }
        }

        /**
         * Supporting user deactivation of the resource load using a filter call
         */
        if (has_filter('essb_resource_builder_deactivate')) {
            $r = apply_filters('essb_resource_builder_deactivate', $r);
        }

        return $r;
    }

    /**
     * Generate code that is required to run on the page header
     *
     * @return void
     */
    public function header()
    {
        do_action('essb_rs_head');
    }

    /**
     * Generate code that is to run on the footer
     *
     * @return void
     */
    public function footer()
    {
        
        // since version 4 we introduce new mail form code added here
        /**
         * @since 8.3 A new loading key mail_form replacing the old mail. This is due to optimizations 
         * where the mail styles are no longer part of the mail CSS.
         */
        if ($this->is_activated('mail_form')) {
            essb_depend_load_function('essb_rs_mailform_build', 'lib/core/resource-snippets/essb_rs_code_mailform.php');
        }

        do_action('essb_rs_footer');
    }


    /**
     * Cloning disabled
     */
    private function __clone() {}

    /**
     * Wakeup disabled (singleton)
     *
     * @return void
     */
    public function __wakeup() {}


    /**
     * Deactivate plugin actions that are responsible for the optimized resource loading
     *
     * @return void
     */
    public function deactivate_actions()
    {
        remove_action('wp_head', array($this, 'header'));
        remove_action('wp_footer', array($this, 'footer'), 999);
        remove_action('wp_enqueue_scripts', array($this, 'register_front_assets'), 10);
    }

    /**
     * Mark a resource as active
     * 
     * @param string $key
     * @return void
     */
    public function activate_resource($key = '')
    {
        ESSB_Static_CSS_Loader::$active_resources[$key] = true;
    }

    /**
     * Check if a resource is already activated
     * 
     * @param string $key
     * @return bool
     */
    public function is_activated($key = '')
    {
        return isset(ESSB_Static_CSS_Loader::$active_resources[$key]);
    }

    /**
     * Remove a script key from the static resource lists
     *
     * @param string $key
     * @return void
     */
    public function remove_static_js_resource($key)
    {
        if (isset($this->js_static_nonasync[$key])) {
            unset($this->js_static_nonasync[$key]);
        }

        if (isset($this->js_static[$key])) {
            unset($this->js_static[$key]);
        }
    }

    /**
     * Add a static resource file (CSS or JS) to the loading queue
     *
     * @param string $file_with_path
     * @param string $key
     * @param string $type
     * @param bool   $noasync
     * @return void
     */
    public function add_static_resource($file_with_path, $key, $type = '', $noasync = false)
    {
        if ($type == 'css') {
            ESSB_Static_CSS_Loader::register_header_style($key, $file_with_path);
        }
        if ($type == 'js') {
            if ($noasync) {
                $this->js_static_nonasync[$key] = $file_with_path;
            } else {
                $this->js_static[$key] = $file_with_path;
            }
        }
    }

    /**
     * Add a static resource file to the footer loading queue
     *
     * @param string $file_with_path
     * @param string $key
     * @param string $type
     * @param bool   $noasync
     * @return void
     */
    public function add_static_resource_footer($file_with_path, $key, $type = '', $noasync = false)
    {
        if ($type == 'css') {
            ESSB_Static_CSS_Loader::register_footer_style($key, $file_with_path);
        }
        if ($type == 'js') {
            if ($noasync) {
                // @since 3.0.4 - double check for the twice counter load script
                if (!isset($this->js_static_nonasync[$key])) {
                    $this->js_static_noasync_footer[$key] = $file_with_path;
                }
            } else {
                // @since 3.0.4 - double check for the twice counter load script
                if (!isset($this->js_static[$key])) {
                    $this->js_static_footer[$key] = $file_with_path;
                }
            }
        }
    }

    /**
     * Add a static CSS file to the footer
     *
     * @param string $file_with_path
     * @param string $key
     * @return void
     */
    public function add_static_footer_css($file_with_path, $key)
    {
        ESSB_Static_CSS_Loader::register_footer_style($key, $file_with_path);
    }

    /**
     * Add inline CSS code to the dynamic builder
     *
     * @param string $code
     * @param string $key
     * @param string $location
     * @return void
     */
    public function add_css($code, $key = '', $location = 'head')
    {
        if ($key != '') {
            ESSB_Dynamic_CSS_Builder::register_dynamic_code($key, $code, $location == 'head' ? 'header' : 'footer');
        } else {
            ESSB_Dynamic_CSS_Builder::register_static_code($code, $location == 'head' ? 'header' : 'footer');
        }
    }

    /**
     * Mark a social network API as loaded
     *
     * @param string $key
     * @return void
     */
    public function add_social_api($key)
    {
        $this->js_social_apis[$key] = 'loaded';
    }

    /**
     * Add inline JavaScript code to the output queue
     *
     * @param string $code
     * @param bool   $minify
     * @param string $key
     * @param string $position
     * @param bool   $noncachble
     * @return void
     */
    public function add_js($code, $minify = false, $key = '', $position = 'footer', $noncachble = false)
    {
        if ($minify) {
            $code = trim(preg_replace('/\s+/', ' ', $code));
        }

        if ($key != '') {
            if ($position == 'footer') {
                if ($noncachble) {
                    $this->js_code_noncachable[$key] = $code;
                } else {
                    $this->js_code[$key] = $code;
                }
            } else {
                $this->js_code_head[$key] = $code;
            }
        } else {
            if ($position == 'footer') {
                if ($noncachble) {
                    $this->js_code_noncachable[] = $code;
                } else {
                    $this->js_code[] = $code;
                }
            } else {
                $this->js_code_head[] = $code;
            }
        }
    }

    /**
     * Enqueue a single CSS file or add it to the precompiled queue
     *
     * @param string $key
     * @param string $file
     * @param string $version
     * @return void
     */
    public function enqueue_style_single_css($key, $file, $version)
    {
        if (!$this->precompile_css) {
            wp_enqueue_style($key, $file, false, $this->resource_version, 'all');

            if ($key == $this->core_style_id() && $this->inline_styles != '') {
                wp_add_inline_style($key, $this->inline_styles);
            }
        } else {
            $this->precompiled_css_queue[$key] = $file;
        }
    }

    /**
     * Get the core style handle
     *
     * @return string
     */
    public function core_style_id()
    {
        return 'easy-social-share-buttons';
    }

    /**
     * Get the core script handle
     *
     * @return string
     */
    public function core_script_id()
    {
        return 'easy-social-share-buttons-core';
    }

    /**
     * Check if the static cache system is active
     *
     * @return bool
     */
    public function is_static_cache_running()
    {
        return class_exists('ESSBStaticCache');
    }

    /**
     * Check if the core style of plugin is loaded to determine the custom resource
     * load mode
     *
     * @return boolean
     */
    public function is_core_style_loaded()
    {
        return !essb_option_bool_value('dont_load_css') && !$this->precompile_css && !$this->inline_css_footer && ESSB_Static_CSS_Loader::style_loaded($this->core_style_id());
    }

    /**
     * Sanitize CSS output by stripping HTML tags
     *
     * @param string $code
     * @return string
     */
    public function sanitize_css_output($code)
    {
        return wp_strip_all_tags($code);
    }

    /**
     * Check if the plugin core script is loaded with the enqueue functions
     *
     * @return bool
     */
    public function is_core_script_loaded()
    {
        return !$this->precompile_js && !$this->js_async && !$this->js_defer && isset($this->js_static[$this->core_script_id()]);
    }

    /**
     * Add pre-compiled cached styles to the header
     *
     * @return void
     */
    public function register_precompile_styles()
    {
        if ($this->plugin_deactivate || $this->precompiled_footer) {
            return;
        }

        $cache_key = $this->get_precompile_cache_key('');

        $cached_data = ESSBPrecompiledResources::get_resource($cache_key, 'css');

        if ($cached_data != '') {
            if ($this->precompile_css_preload) {
                $this->precompile_css_filename = $cached_data;
                add_action('wp_head', array($this, 'preload_css_styles'));
            } else {
                wp_enqueue_style('essb-compiledcache', $cached_data, false, $this->resource_version, 'all');
                $this->precompile_css_loaded = true;
            }
        }
    }

    /**
     * Output preload link for precompiled CSS file
     *
     * @return void
     */
    public function preload_css_styles()
    {
        if ($this->precompile_css_filename != '') {
            essb_manual_preload_css_file($this->precompile_css_filename);
        }
    }

    /**
     * Build a consistent cache key for precompiled resources
     *
     * @param string $suffix
     * @return string
     */
    private function get_precompile_cache_key($suffix = '')
    {
        $key = 'essb-precompiled' . (essb_is_mobile() ? '-mobile' : '');

        if (essb_option_bool_value('precompiled_unique')) {
            $key .= ESSBPrecompiledResources::get_unique_identifier();
        }

        if (essb_option_bool_value('precompiled_post')) {
            $post_key = essb_get_page_id();
            if ($post_key) {
                $key .= '-' . $post_key;
            }
        }

        return $key . $suffix;
    }

    /**
     * Fix relative asset URLs in precompiled CSS based on the style key
     *
     * @param string $key
     * @param string $css_code
     * @return string
     */
    private function fix_css_asset_urls($key, $css_code)
    {
        switch ($key) {
            case 'essb-social-image-share':
                return str_replace('../', ESSB3_PLUGIN_URL . '/lib/modules/social-image-share/assets/', $css_code);
            case 'easy-social-share-buttons-profiles':
            case 'easy-social-share-buttons-display-methods':
            case 'easy-social-share-buttons':
                return str_replace('../', ESSB3_PLUGIN_URL . '/assets/', $css_code);
            case 'essb-social-followers-counter':
                return str_replace('../', ESSB3_PLUGIN_URL . '/lib/modules/social-followers-counter/assets/', $css_code);
            default:
                return $css_code;
        }
    }

    /**
     * Register frontend CSS and JavaScript assets
     *
     * @return void
     */
    public function register_front_assets()
    {
        if ($this->plugin_deactivate) {
            return;
        }

        /**
         * Loading inline the custom plugin styles. They will load based on the settings for optimization of the user
         */
        do_action('essb_rs_head_enqueue');

        ESSB_Part_CSS_Manager::register_keys_by_settings();

        $load_in_footer = ($this->js_head) ? false : true;

        /**
         * @since 8.0 replace use_stylebuilder with dont_load_css
         */
        if (!essb_option_bool_value('dont_load_css')) {
            // enqueue all css registered files

            /**
             * Registering static CSS styles. The core style ID will be loaded
             * last in the queue to prevent visual issues
             */
            $loading_core = array('active' => false, 'key' => '', 'file' => '');

            /**
             * @since 11.5 Part CSS will be loaded after main style
             */
            $share_part_css_loading_after_core = array();

            foreach (ESSB_Static_CSS_Loader::get('header') as $key => $file) {
                if ($key == $this->core_style_id()) {
                    $loading_core['active'] = true;
                    $loading_core['key'] = $key;
                    $loading_core['file'] = $file;
                    continue;
                }
            }


            if ($loading_core['active']) {
                /**
                 * @since 11.5
                 */
                ESSB_Part_CSS_Manager::load_all_pending_share();
            }

            /**
             * @since 11.5
             */
            ESSB_Part_CSS_Manager::load_all_pending_other();

            foreach (ESSB_Static_CSS_Loader::get('header') as $key => $file) {
                if ($key == $this->core_style_id()) {
                    continue;
                }

                if (strpos($file, '/assets/parts/') !== false && strpos($key, 'sharing-') !== false) {
                    $share_part_css_loading_after_core[$key] = $file;
                    continue;
                }

                if ($key == 'easy-social-share-buttons-profles') {
                    if (!essb_is_module_deactivated_on('profiles')) {
                        $this->enqueue_style_single_css($key, $file, $this->resource_version);
                    }
                } else if ($key == 'easy-social-share-buttons-nativeskinned' || $key == 'essb-fontawsome' || $key == 'essb-native-privacy') {
                    if (!essb_is_module_deactivated_on('native')) {
                        $this->enqueue_style_single_css($key, $file, $this->resource_version);
                    }
                } else {
                    $this->enqueue_style_single_css($key, $file, $this->resource_version);
                }
            }

            if ($loading_core['active']) {
                $this->enqueue_style_single_css($loading_core['key'], $loading_core['file'], $this->resource_version);

                foreach ($share_part_css_loading_after_core as $key => $file) {
                    $this->enqueue_style_single_css($key, $file, $this->resource_version);
                }
            }
        } else {
            // using build in builder styles
            $user_styles = essb_option_value('stylebuilder_css');
            if (!is_array($user_styles)) {
                $user_styles = array();
            }

            if ($user_styles) {
                $this->enqueue_style_single_css('essb-userstyles', content_url('easysocialsharebuttons-assets/essb-userselection.min.css'), $this->resource_version);
            }
        }

        foreach ($this->js_static_nonasync as $key => $file) {
            wp_enqueue_script($key, $file, array('jquery'), $this->resource_version, $load_in_footer);
        }

        // load scripts when no async or deferred is selected
        if (!$this->js_async && !$this->js_defer) {
            foreach ($this->js_static as $key => $file) {
                if (!$this->precompile_js) {
                    wp_enqueue_script($key, $file, array('jquery'), $this->resource_version, $load_in_footer);
                } else {
                    $this->precompiled_js_queue[$key] = $file;
                }
            }
        }

        /**
         * reCAPTCHA
         */
        if ($this->should_load_recaptcha()) {
            essb_depend_load_function('essb_rs_mailform_build', 'lib/core/resource-snippets/essb_rs_code_mailform.php');
            essb_register_mail_recaptcha();
        }
    }


    /**
     * Compile and output the custom CSS generated by plugin settings changes
     *
     * @return void
     */
    public function generate_custom_css()
    {
        if ($this->plugin_deactivate) {
            return;
        }

        $cache_slug = 'essb-css-head';

        if (defined('ESSB3_CACHE_ACTIVE_RESOURCE')) {
            global $post;

            if (isset($post)) {
                $cache_key = $cache_slug . $post->ID;

                if (essb_dynamic_cache_load_css($cache_key)) {
                    return;
                }
            }
        }


        $css_code = '';
        $css_code = apply_filters('essb_css_buffer_head', $css_code);
        $css_code .= ESSB_Dynamic_CSS_Builder::compile_header();

        if ($css_code != '') {
            /**
             * Checking the current cache state. If the internal cache runs than the plugin
             * will store the data in a static resource
             */
            if (defined('ESSB3_CACHE_ACTIVE_RESOURCE')) {
                if (isset($post)) {
                    $cache_key = $cache_slug . $post->ID;
                    ESSBDynamicCache::put_resource($cache_key, $css_code, 'css');
                    essb_dynamic_cache_load_css($cache_key);
                    return;
                }
            }

            if ($this->is_core_style_loaded()) {
                $this->inline_styles = $css_code;
            } else {
                echo '<style type="text/css" id="easy-social-share-buttons-inline-css" media="all">' . $css_code . '</style>';
            }
        }
    }

    /**
     * Generate additional CSS code in the page footer (below the fold)
     *
     * @return void
     */
    public function generate_custom_footer_css()
    {
        global $post;

        if ($this->plugin_deactivate) {
            return;
        }

        foreach (ESSB_Static_CSS_Loader::get('footer') as $key => $file) {
            printf('<link rel="stylesheet" id="%1$s" href="%2$s" type="text/css" media="all" />', $key, esc_url($file));
        }


        $cache_slug = 'essb-css-footer';
        if (isset($post)) {
            if (defined('ESSB3_CACHE_ACTIVE_RESOURCE')) {
                $cache_key = $cache_slug . $post->ID;

                if (essb_dynamic_cache_load_css($cache_key)) {
                    return;
                }
            }
        }

        $css_code = '';

        $css_code .= apply_filters('essb_css_buffer_footer', $css_code);
        $css_code .= ESSB_Dynamic_CSS_Builder::compile_footer();

        if ($css_code != '') {
            if (isset($post)) {

                if (defined('ESSB3_CACHE_ACTIVE_RESOURCE')) {
                    $cache_key = $cache_slug . $post->ID;

                    ESSBDynamicCache::put_resource($cache_key, $css_code, 'css');

                    essb_dynamic_cache_load_css($cache_key);
                    return;
                }
            }
            echo '<style type="text/css" id="easy-social-share-buttons-footer-inline-css" media="all">' . $css_code . '</style>';
        }
    }

    /**
     * Generate and cache pre-compiled static CSS resources.
     * The compile action will execute only if the cache is not present or not existing.
     *
     * @return void
     */
    public function generate_custom_css_precompiled()
    {
        if ($this->plugin_deactivate) {
            return;
        }

        if ($this->precompile_css_loaded) {
            return;
        }

        $cache_key = $this->get_precompile_cache_key('');

        $cached_data = ESSBPrecompiledResources::get_resource($cache_key, 'css');

        if ($cached_data != '') {
            if ($this->precompile_css_preload) {
                essb_manual_preload_css_file($cached_data);
                return;
            } else {
                echo "<link rel='stylesheet' id='essb-compiledcache-css'  href='" . esc_url($cached_data) . "' type='text/css' media='all' />";
                return;
            }
        }

        /**
         * @since 8.7
         * @var array $loaded_urls Prevent loading duplicated files in the cache
         */
        $loaded_urls = array();

        $static_content = array();
        $styles = array();

        $css_code = '';
        $css_code = apply_filters('essb_css_buffer_head', $css_code);
        $css_code .= ESSB_Dynamic_CSS_Builder::compile_header();

        $styles[] = $css_code;

        // parsing inlinde enqueue styles
        $current_site_url = get_site_url();
        foreach ($this->precompiled_css_queue as $key => $file) {

            if (in_array($file, $loaded_urls)) {
                continue;
            }

            $relative_path = ESSBPrecompiledResources::get_asset_relative_path($current_site_url, $file);
            $css_code = file_get_contents(ABSPATH . $relative_path);
            $css_code = $this->fix_css_asset_urls($key, $css_code);

            $styles[] = $css_code;

            $static_content[$key] = $file;
            $loaded_urls[] = $file;
        }

        foreach (ESSB_Static_CSS_Loader::get('footer') as $key => $file) {
            if (in_array($file, $loaded_urls)) {
                continue;
            }

            $relative_path = ESSBPrecompiledResources::get_asset_relative_path($current_site_url, $file);
            $css_code = file_get_contents(ABSPATH . $relative_path);
            $css_code = $this->fix_css_asset_urls($key, $css_code);

            $styles[] = $css_code;

            $static_content[$key] = $file;
            $loaded_urls[] = $file;
        }

        $css_code = '';

        $css_code .= apply_filters('essb_css_buffer_footer', $css_code);
        $css_code .= ESSB_Dynamic_CSS_Builder::compile_footer();

        $styles[] = $css_code;

        $toc = array();

        foreach ($static_content as $handle => $item_content)
            $toc[] = sprintf(' - %s', $handle . '-' . $item_content);
        if (essb_option_bool_value('precompiled_post')) {
            $post_key = essb_get_page_id();
            if ($post_key) {
                $toc[] = ' - Post ID: ' . $post_key;
            }
        }

        $styles[] = sprintf("\n\n\n/* TOC:\n%s\n*/", implode("\n", $toc));

        ESSBPrecompiledResources::put_resource($cache_key, implode(' ', $styles), 'css');

        $cached_data = ESSBPrecompiledResources::get_resource($cache_key, 'css');

        if ($cached_data != '') {
            if ($this->precompile_css_preload) {
                essb_manual_preload_css_file($cached_data);
                return;
            } else {
                echo "<link rel='stylesheet' id='essb-compiledcache-css' href='" . esc_url($cached_data) . "' type='text/css' media='all' />";
                return;
            }
        }
    }


    /**
     * Generate and cache pre-compiled static JavaScript resources.
     * Non-cachable and non-async scripts are loaded separately.
     *
     * @return void
     */
    public function generte_custom_js_precompiled()
    {
        if ($this->plugin_deactivate) {
            return;
        }

        // -- loading non cachble and noasync code first
        if ($this->js_social_apis) {
            if (!essb_is_module_deactivated_on('native')) {
                essb_depend_load_function('essb_load_social_api_code', 'lib/core/resource-snippets/essb-rb-socialapi.php');
                foreach ($this->js_social_apis as $network => $loaded) {
                    essb_load_social_api_code($network);
                }
            }
        }

        if ($this->js_static_noasync_footer) {
            foreach ($this->js_static_noasync_footer as $key => $file) {
                $this->manual_script_load($key, $file);
            }
        }

        // loading in precompiled cache mode
        $cache_key = $this->get_precompile_cache_key('');

        $cached_data = ESSBPrecompiledResources::get_resource($cache_key, 'js');

        /**
         * Check for already stored cache. If so than the code will generate the static link to the file
         * and stop further loading
         */
        if ($cached_data != '') {
            echo "<script type='text/javascript' src='" . esc_url($cached_data) . "' async></script>";
            return;
        }

        $static_content = array();
        $scripts = array();
        $current_site_url = get_site_url();

        $scripts[] = implode(" ", $this->js_code);

        if ($this->js_static) {
            foreach ($this->js_static as $key => $file) {
                $relative_path = ESSBPrecompiledResources::get_asset_relative_path($current_site_url, $file);
                $code = file_get_contents(ABSPATH . $relative_path);

                $scripts[] = $code;

                $static_content[$key] = $file;
            }
        }

        if ($this->js_static_footer) {
            foreach ($this->js_static_footer as $key => $file) {
                $relative_path = ESSBPrecompiledResources::get_asset_relative_path($current_site_url, $file);
                $code = file_get_contents(ABSPATH . $relative_path);

                $scripts[] = $code;

                $static_content[$key] = $file;
            }
        }

        $js_code = '';
        $js_code = apply_filters('essb_js_buffer_footer', $js_code);
        $scripts[] = $js_code;

        $toc = array();

        foreach ($static_content as $handle => $item_content)
            $toc[] = sprintf(' - %s', $handle . '-' . $item_content);

        $scripts[] = sprintf("\n\n\n/* TOC:\n%s\n*/", implode("\n", $toc));

        ESSBPrecompiledResources::put_resource($cache_key, implode(' ', $scripts), 'js');

        $cached_data = ESSBPrecompiledResources::get_resource($cache_key, 'js');

        if ($cached_data != '') {
            echo "<script type='text/javascript' src='" . esc_url($cached_data) . "' async></script>";
        }
    }

    /**
     * Generate the plugin setup JavaScript that loads custom setup scripts. They are critical and non-cached.
     *
     * @return void
     */
    public function generate_custom_js()
    {
        if ($this->plugin_deactivate) {
            return;
        }

        $js_code = '';

        if ($this->js_code_head) {
            $js_code = implode('', $this->js_code_head);
        }
        $js_code = apply_filters('essb_js_buffer_head', $js_code);

        if ($this->is_core_script_loaded() && $js_code != '' && !$this->is_static_cache_running()) {
            wp_add_inline_script($this->core_script_id(), $js_code);
        } else if ($js_code != '') {
            echo "\n";
            echo sprintf('<script type="text/javascript">%1$s</script>', $js_code);
        }
    }

    /**
     * Load additional footer scripts including social APIs, static scripts, and dynamic JS code
     *
     * @return void
     */
    public function generate_custom_footer_js()
    {
        global $post;

        /**
         * Validating plugin runtime. If plugin is deactivated we will not continue
         * to run anymore
         */
        if ($this->plugin_deactivate) {
            return;
        }

        $cache_slug = "essb-js-footer";
        if ($this->js_social_apis) {
            if (!essb_is_module_deactivated_on('native')) {
                essb_depend_load_function('essb_load_social_api_code', 'lib/core/resource-snippets/essb-rb-socialapi.php');
                foreach ($this->js_social_apis as $network => $loaded) {
                    essb_load_social_api_code($network);
                }
            }
        }

        /**
         * Optimized script include as async or deferred (if active from control panel)
         */
        if ($this->js_static) {
            if ($this->js_defer || $this->js_async) {
                essb_load_static_script($this->js_static, $this->js_async);
            }
        }

        /**
         * Loading additional scripts that are included on the runtime process
         */
        if ($this->js_static_footer) {
            if ($this->js_defer || $this->js_async) {
                essb_load_static_script($this->js_static_footer, $this->js_async);
            } else {
                foreach ($this->js_static_footer as $key => $file) {
                    $this->manual_script_load($key, $file);
                }
            }
        }

        /**
         * Critical non-async scripts (async may prevent plugin from work)
         */
        if ($this->js_static_noasync_footer) {
            foreach ($this->js_static_noasync_footer as $key => $file) {
                $this->manual_script_load($key, $file);
            }
        }

        if ($this->js_code_noncachable) {
            echo implode(" ", $this->js_code_noncachable);
        }

        // dynamic footer javascript that can be cached
        $cache_slug = "essb-js-footer";

        if (isset($post)) {
            if (defined('ESSB3_CACHE_ACTIVE_RESOURCE')) {
                $cache_key = $cache_slug . $post->ID;

                if (essb_dynamic_cache_load_js($cache_key)) {
                    return;
                }
            }
        }


        $js_code = implode('', $this->js_code);
        $js_code = apply_filters('essb_js_buffer_footer', $js_code);
        if (isset($post)) {
            if (defined('ESSB3_CACHE_ACTIVE_RESOURCE')) {
                $cache_key = $cache_slug . $post->ID;

                ESSBDynamicCache::put_resource($cache_key, $js_code, 'js');

                essb_dynamic_cache_load_js($cache_key);
                return;
            }
        }
        echo '<script type="text/javascript">' . $js_code . '</script>';
    }

    /**
     * Manually enqueue a script tag for a static resource
     *
     * @param string $key
     * @param string $file
     * @return void
     */
    public function manual_script_load($key, $file)
    {
        $ver_string = "";

        if (!empty($this->resource_version)) {
            $ver_string = "?ver=" . $this->resource_version;
        }

        essb_manual_script_load($key, $file, $ver_string);
    }

    /**
     * Output priority loaded inline CSS and JavaScript in the page head
     *
     * @return void
     */
    public function header_priority_load()
    {
        if ($this->plugin_deactivate) {
            return;
        }

        echo '<script id="essb-inline-js">';
        do_action('essb_inline_js_queue');
        echo '</script>';

        echo '<style type="text/css" id="essb-inline-css">';
        do_action('essb_inline_css_queue');
        echo '</style>';
    }

    /**
     * Load an inline static resource. In precompiled mode the resource is registered
     * for the footer so the cache can catch it. Otherwise it is output immediately.
     *
     * @param string $file_with_path
     * @param string $key
     * @param bool   $async_load
     * @return void
     */
    /**
     * Register a part CSS file from the assets/parts directory as a static enqueued resource.
     *
     * @param string $key
     * @return void
     */
    public function add_part_css($key)
    {
        $url = ESSB3_PLUGIN_URL . '/assets/parts/' . $key . '.min.css';
        $this->add_static_resource($url, 'essb-part-' . $key, 'css');
    }

    /**
     * Load a part CSS file from the assets/parts directory and delegate to load_inline_static_resource.
     *
     * @param string $key
     * @param bool   $async
     * @return void
     */
    public function load_inline_part_css($key, $async = false)
    {
        $url = ESSB3_PLUGIN_URL . '/assets/parts/' . $key . '.min.css';
        $this->load_inline_static_resource($url, $key, $async);
    }

    /**
     * Load an inline static resource. In precompiled mode the resource is registered
     * for the footer so the cache can catch it. Otherwise it is output immediately.
     *
     * @param string $file_with_path
     * @param string $key
     * @param bool   $async_load
     * @return void
     */
    public function load_inline_static_resource($file_with_path, $key, $async_load = false)
    {
        if ($this->precompile_css) {
            ESSB_Static_CSS_Loader::register_footer_style($key, $file_with_path);
        } else {
            ESSB_Static_CSS_Loader::load_inline_style($key, $file_with_path, $async_load);
        }
    }
}

/**
 * Output a preload link for a CSS file with onload stylesheet activation
 *
 * @param string $url
 * @return void
 */
if (!function_exists('essb_manual_preload_css_file')) {
    function essb_manual_preload_css_file($url = '')
    {
        echo '<link rel="preload" href="' . esc_url($url) . '" as="style" onload="this.rel=\'stylesheet\'">';
    }
}

/**
 * Manually output a script tag
 *
 * @param string $key
 * @param string $file
 * @param string $ver_string
 * @return void
 */
if (!function_exists('essb_manual_script_load')) {
    function essb_manual_script_load($key, $file, $ver_string = '')
    {
        echo '<script type="text/javascript" src="' . $file . $ver_string . '"></script>';
    }
}

/**
 * Load cached CSS resource by key and output the link tag if found
 *
 * @param string $cache_key
 * @return bool
 */
if (!function_exists('essb_dynamic_cache_load_css')) {
    function essb_dynamic_cache_load_css($cache_key = '')
    {
        $cached_data = ESSBDynamicCache::get_resource($cache_key, 'css');

        if ($cached_data != '') {
            echo "<link rel='stylesheet' id='" . esc_attr($cache_key) . "' href='" . esc_url($cached_data) . "' type='text/css' media='all' />";
            return true;
        } else {
            return false;
        }
    }
}

/**
 * Load cached JavaScript resource by key and output the script tag if found
 *
 * @param string $cache_key
 * @return bool
 */
if (!function_exists('essb_dynamic_cache_load_js')) {
    function essb_dynamic_cache_load_js($cache_key)
    {
        $cached_data = ESSBDynamicCache::get_resource($cache_key, 'js');

        if ($cached_data != '') {
            echo "<script type='text/javascript' src='" . esc_url($cached_data) . "' defer></script>";
            return true;
        } else {
            return false;
        }
    }
}

/**
 * Output inline script tags for async or deferred script loading
 *
 * @param array $list
 * @param bool  $async
 * @return void
 */
if (!function_exists('essb_load_static_script')) {
    function essb_load_static_script($list, $async)
    {
        $result = '';
        $load_mode = ($async) ? "po.async=true;" : "po.defer=true;";

        foreach ($list as $key => $file) {
            $result .= ('(function() { var po = document.createElement(\'script\'); po.type = \'text/javascript\'; ' . $load_mode . '; po.src = \'' . esc_url($file) . '\'; var s = document.getElementsByTagName(\'script\')[0]; s.parentNode.insertBefore(po, s); })();');
        }

        if ($result != '') {
            echo '<script type="text/javascript">' . $result . '</script>';
        }
    }
}
