<?php
/*
    css and customizer processor module
    integrated in core theme
*/
if( ! defined( 'ABSPATH' ) ) exit;

new processor();
class processor {

    // basic control and processor vars
    public static $vars = array();
    public static $mods = array();
    public static $root = array();

    // Fonts
	public static $fontInfosProcessed = false;
    public static $cfontsInfo = array();
    public static $cfonts = array();
    public static $gfontsInfo = array();
    public static $gfonts = array();
    public static $wfonts = array();
    public static $lazy_fonts = array();

    /* 
        Constructor 
    */
    function __construct() {
        // get all controls
        processor::$root = controls::$root;
        // get fonts
        add_action( 'get_header', array($this, 'process_defaults'), 99 );
        add_action( 'wp_print_styles', array($this, 'load_scripts'), 99 );
        add_action( 'wp_footer', array($this, 'enqueue_lazy_fonts'), 99 );
    }

	/**
	 * Get infos for all 3 font types:
	 * * (1) Custom fonts
	 * * (2) Google fonts
	 * * (3) Web fonts
	 * 
	 * Save infos in static vars:
	 * * (1) @var array cfonts | @var array cfontsInfo
	 * * (2) @var array gfonts | @var array gfontsInfo
	 * * (3) @var array wfonts
	 */
	public static function get_font_infos() {

		if ( processor::$fontInfosProcessed ) return;

		/**
		 * (1) Get custom font infos
		 */
		if ( ! apply_filters( 'greyd_suite_disable_custom_fonts', false ) ) {
			$uploads     = wp_upload_dir();
			$fontsfile   = $uploads['basedir']."/greyd_tp/custom_fonts/custom_fonts_index.json";

			/**
			 * Get correctly pathed uploads url even in multisite installations.
			 * 
			 * @since 1.7.6
			 */
			$path        = str_replace( WP_CONTENT_DIR, '', $uploads['basedir'] );
			$uploads_url = \vc\helper::maybe_make_https( content_url( $path ) );

			if (file_exists($fontsfile)) {
				$fonts = json_decode(\vc\helper::get_file_contents($fontsfile));
				// debug($fonts->fonts);
				if ( $fonts && $fonts->fonts ) {
					foreach ($fonts->fonts as $font) {
						processor::$cfonts[$font->name_full] = $font->name_full;
						processor::$cfontsInfo[$font->name_full] = array(
							'name' => $font->name,
							'file' => $uploads_url."/greyd_tp/custom_fonts/".$font->file
						);
					}
				}
			}
		}

		/**
		 * (2) Get google font infos
		 */
		if ( ! apply_filters( 'greyd_suite_disable_google_fonts', false ) ) {
			$gfontsindex = get_template_directory()."/assets/font/google_fonts_index.json";
			$gfontsdetails = get_template_directory()."/assets/font/google_fonts_details.json";
			if (file_exists($gfontsindex)) {
				// read fonts list
				$gfontslist = (array)json_decode(\vc\helper::get_file_contents($gfontsindex));
				$gfontsinfo = (array)json_decode(\vc\helper::get_file_contents($gfontsdetails));
				if ($gfontslist == "" || count($gfontslist) == 0) {
					// get full webfonts details from api
					$gfontsapi = "https://www.googleapis.com/webfonts/v1/webfonts?key=AIzaSyBLBeqjz4y-yYybCig6p1PMKnt9g4PLLNU";
					$gfontsfile = get_template_directory()."/assets/font/google_fonts_full.json";
					$gfontsfull = json_decode(file_get_contents($gfontsapi));
					// // save full list
					// $fo = fopen($gfontsfile, 'w');
					// fwrite($fo, json_encode($gfontsfull, JSON_PRETTY_PRINT));
					// fclose($fo);
					// make index and details list
					$gfontslist = array();
					$gfontsinfo = array();
					if ( is_object($gfontsfull) ) {
						foreach ($gfontsfull->items as $gfont) {
							$name = $gfont->family;
							array_push($gfontslist, $name);
							$gfontsinfo[$name] = array( 
								"category" => $gfont->category, 
								"variants" => $gfont->variants 
							);
						}
					}
					// save index list
					$fo = fopen($gfontsindex, 'w');
					fwrite($fo, json_encode($gfontslist));
					fclose($fo);
					// save details list
					$fo = fopen($gfontsdetails, 'w');
					fwrite($fo, json_encode($gfontsinfo));
					fclose($fo);
				}
				foreach ($gfontslist as $gfont) {
					processor::$gfonts[$gfont] = $gfont;
					processor::$gfontsInfo[$gfont] = $gfontsinfo[$gfont];
				}
			}
		}
		
		/**
		 * (3) Get webfont infos
		 */
		if ( ! apply_filters( 'greyd_suite_disable_web_fonts', false ) ) {
			if (count(processor::$wfonts) == 0) {
				processor::$wfonts = array(
					// Sans-Serif Fonts
					'Arial, Helvetica, sans-serif' => 'Arial, Helvetica, sans-serif',
					'"Arial Black", Gadget, sans-serif' => '"Arial Black", Gadget, sans-serif',
					'"Comic Sans MS", cursive, sans-serif' => '"Comic Sans MS", cursive, sans-serif',
					'Impact, Charcoal, sans-serif' => 'Impact, Charcoal, sans-serif',
					'"Lucida Sans Unicode", "Lucida Grande", sans-serif' => '"Lucida Sans Unicode", "Lucida Grande", sans-serif',
					'Tahoma, Geneva, sans-serif' => 'Tahoma, Geneva, sans-serif',
					'"Trebuchet MS", Helvetica, sans-serif' => '"Trebuchet MS", Helvetica, sans-serif',
					'Verdana, Geneva, sans-serif' => 'Verdana, Geneva, sans-serif',
					// Serif Fonts
					'Georgia, serif' => 'Georgia, serif',
					'"Palatino Linotype", "Book Antiqua", Palatino, serif' => '"Palatino Linotype", "Book Antiqua", Palatino, serif',
					'"Times New Roman", Times, serif' => '"Times New Roman", Times, serif',
					// Monospace Fonts
					'"Courier New", Courier, monospace' => '"Courier New", Courier, monospace',
					'"Lucida Console", Monaco, monospace' => '"Lucida Console", Monaco, monospace',
				);
			}
		}

		processor::$fontInfosProcessed = true;
	}

    public static function process_defaults() {
        // if (count(processor::$mods) > 0) return;

		processor::get_font_infos();
        
        // @media hack
        processor::$mods['announcement_toggle'] = get_theme_mod( 'announcement_toggle', 1024 );
        processor::$mods['branding_toggle'] = get_theme_mod( 'branding_toggle', 768 );
        processor::$mods['menu_toggle'] = get_theme_mod( 'menu_toggle', 768 );
        processor::$mods['grid_maxwidth'] = get_theme_mod( 'grid_maxwidth', '1620px' );
        if (processor::$mods['menu_toggle'] == 1921) processor::$mods['menu_toggle'] = 99999;
        processor::$mods['meta_menu_toggle'] = get_theme_mod( 'meta_menu_toggle', 768 );
        if (processor::$mods['meta_menu_toggle'] == 1921) processor::$mods['meta_menu_toggle'] = 99999;
        processor::$mods['navi_header_mobile_toggle'] = get_theme_mod( 'navi_header_mobile_toggle', 768 );
        if (processor::$mods['navi_header_mobile_toggle'] == 1921) processor::$mods['navi_header_mobile_toggle'] = 99999;
        
        // convert colors
        if (!get_theme_mod('enable_colorconcept', false)) {
            $light = get_theme_mod( 'light_color', '#dddddd' );
            $lightSec = get_theme_mod( 'lightSec_color', '#fcfcfc' );
            $dark = get_theme_mod( 'dark_color', '#303030' );
            $darkSec = get_theme_mod( 'darkSec_color', '#777777' );
            // Branding Farben
            set_theme_mod('color11', $darkSec);
            set_theme_mod('color12', \vc\helper::hexavg($darkSec, $light, 0.5));
            set_theme_mod('color13', \vc\helper::hexavg($darkSec, $dark, 0.5));
            // Dunkle Farben
            set_theme_mod('color31', $dark);
            set_theme_mod('color21', \vc\helper::hexavg($dark, $light, 0.33));
            set_theme_mod('color32', \vc\helper::hexavg($dark, $light, 0.66));
            // Helle Farben
            set_theme_mod('color22', $light);
            set_theme_mod('color33', \vc\helper::hexavg($light, $lightSec, 0.5));
            set_theme_mod('color23', $lightSec);
            // enable colors 2.0
            set_theme_mod('enable_colorconcept', true);
            if (count(get_theme_mods()) < 15)
                update_option('fresh_site', '1');
        }

        // init vars
        $theme_mods = get_theme_mods();
        foreach (processor::$root as $var => $val) {
            if ($val['type'] == 'separator' || 
                $val['type'] == 'sub_separator' || 
                $val['type'] == 'info' || 
                $val['type'] == 'group' || 
                $val['type'] == 'button' ||
                $val['type'] == 'preset' ||
                !isset($val['default'])) continue;
            // get mod
            if (is_customize_preview())
                $value = get_theme_mod( $val['mod'], null );
            else
                $value = isset($theme_mods[$val['mod']]) ? $theme_mods[$val['mod']] : null;
            // check for value and dependency
            if ( $value == null || (isset($val['dependency']) && $val['dependency'] != null && !processor::calcDependencies($val['dependency'])) ) {
                // reset to default
                $value = $val['default'];
            }
            processor::$mods[$var] = $value;
        }
        // compute vars
        foreach (processor::$mods as $var => $val) {
            if (!isset(processor::$root[$var])) continue;
            $root_value = processor::$root[$var];
            $value = $val;
            // replace nested vars
            if (strpos($value, '--') > -1) {
                $value = processor::$mods[$value];
            }

            // add theme mod vars
            $mod_value = $value;
            if (isset($root_value['unit'])) $mod_value = str_replace($root_value['unit'], '', $value).$root_value['unit'];

            /**
             * Convert lineHeight into unitless values
             * 
             * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height#prefer_unitless_numbers_for_line-height_values
             * @since 0.9.8
             */
            if ( is_string($var) && strpos($var, 'lineHeight') !== false ) {
                // values: 140, 120%, 110, 90, 90%
                if ( preg_match('/^\d{2,3}\%?$/', strval($value) ) ) {
                    $value = strval(intval($value) / 100);
                }
                // values: 1.4, 1, 0.9
                else {
                    $value = strval(floatval($value));
                }
                $mod_value = $value;
            }

            processor::$mods[$var] = $mod_value;
            processor::$root[$var]['value'] = $value;

            if ($root_value['type'] == 'fontfamily') {
                // calculate gfonts
                if ($value != '') {
                    $tmp = explode(':', $value);
                    if (isset(processor::$gfontsInfo[$tmp[0]])) {
                        $font = $tmp[0];
                        $category = processor::$gfontsInfo[$tmp[0]]->category;
                        if ($category) {
                            if ($category == "display" || $category == "handwriting") 
                                $category = "cursive";
                            $font .= ', '.$category;
                        }
                        processor::$mods[$var] = $font;
                        // processor::$root[$var]['value'] = $font;
                    }
                }
            }
            if ($root_value['type'] == 'shadow') {
                // calculate box-shadow colors
                if ($value != 'none' && strpos($value, 'color') > -1) {
                    $value = str_replace('+', ' ', $value);
                    $tmp = explode("color", $value);
                    $old = "color".$tmp[1];
                    $tmp = explode(" ", $tmp[1]);
                    $sdw = array(
                        'color' => processor::$mods["--color".$tmp[0]],
                        'opacity' => $tmp[1],
                    );
                    if (\vc\helper::isHex($sdw['color'])) {
                        $color = \vc\helper::hex2RGB($sdw['color']);
                        $color['a'] = $sdw['opacity'] / 100.0;
                    }
                    else {
                        if ($sdw['color'] == 'transparent') $color = array( 'r' => 0, 'g' => 0, 'b' => 0, 'a' => 0 );
                        else $color = \vc\helper::rgb2RGB($sdw['color']);
                        $color['a'] = $color['a'] * ($sdw['opacity'] / 100.0);
                    }
                    $new = "rgba(".$color['r'].",".$color['g'].",".$color['b'].",".$color['a'].")";
                    $value = str_replace($old, $new, $value);
                    processor::$mods[$var] = $value;
                    processor::$root[$var]['value'] = $value;
                }
            }
        }
        // debug(processor::$root);
        // debug(processor::$mods);
    }
    public static function calcDependencies($dependency) {
        if ($dependency == null) return false;
        // old dependency style (single element without value)
        if (is_string($dependency)) {
            return processor::calcDependency( array( 'element' => $dependency, 'not_value' => "" ) );
        }
        // single dependency
        else if (isset($dependency['element']) && $dependency['element'] !== null) {
            return processor::calcDependency($dependency);
        }
        // multi dependency
        else if (isset($dependency) && is_array($dependency)) {
            $hits = 0;
            $count = 0;
            foreach ($dependency as $key => $m_dependency) {
                if ($m_dependency != 'and' && $m_dependency != 'or') {
                    if (processor::calcDependencies($m_dependency)) $hits++;
                    $count++;
                }
            }
            $display = false;
            if (!isset($dependency['operator'])  || $dependency['operator'] == 'and') {
                if ($hits == $count) $display = true;
            }
            else if (isset($dependency['operator']) && $dependency['operator'] == 'or') {
                if ($hits > 0) $display = true;
            }
            return $display;
        }
    }
    public static function calcDependency($dependency) {
        $display = false;
        $mod = processor::getMod($dependency['element']);
        while (strpos($mod['default'], '--') > -1) {
            $mod = processor::$root[$mod['default']];
        }
        $value = get_theme_mod($dependency['element'], $mod['default']);
        if ($value !== null) {
            if (isset($dependency['value']) && $dependency['value'] !== null) {
                $dep_value = $dependency['value'];
                if (!is_array($dep_value)) $dep_value = [ $dep_value ];
                for ($j=0; $j<count($dep_value); $j++) {
                    if ($value == $dep_value[$j]) $display = true;
                }
            }
            else if (isset($dependency['not_value']) && $dependency['not_value'] !== null) {
                $dep_value = $dependency['not_value'];
                if (!is_array($dep_value)) $dep_value = [ $dep_value ];
                $hits = 0;
                for ($j=0; $j<count($dep_value); $j++) {
                    if ($value != $dep_value[$j]) $hits++;
                }
                if ($hits == count($dep_value)) $display = true;
            }
            // range text textarea
            else if (isset($dependency['smaller']) && $dependency['smaller'] !== null) {
                $dep_value = $dependency['smaller'];
                if ($mod['type'] == 'text' || $mod['type'] == 'textarea') { 
                    if (count($value) < $dep_value) $display = true; 
                }
                else if ($mod['type'] == 'range') { 
                    if ($value < $dep_value) $display = true; 
                }
            }
            else if (isset($dependency['bigger']) && $dependency['bigger'] !== null) {
                $dep_value = $dependency['bigger'];
                if ($mod['type'] == 'text' || $mod['type'] == 'textarea') { 
                    if (count($value) > $dep_value) $display = true; 
                }
                else if ($mod['type'] == 'range') { 
                    if ($value > $dep_value) $display = true; 
                }
            }
        }
		return $display;
    }
	public static function getMod($mod) {
        foreach (processor::$root as $var => $val) {
            if ($val['mod'] == $mod) return $val;
        }
        return false;
	}

    public static function load_scripts() {
        
        // load google and custom font styles
        $allgfonts = array();
        $allcfonts = array();
        // search for fontfamily type mods
        foreach (self::$root as $var => $val) {
            if ($val['type'] == 'fontfamily') {
                // debug($var);
                // debug($val);
                if (isset($val['value']) && $val['value'] != '') {
                    $tmp = explode(':', $val['value']);
                    if (in_array($val['value'], self::$cfonts)) {
                        $allcfonts[] = $val['value'];
                    } elseif (isset(self::$gfonts[$tmp[0]])) {
                        $allgfonts[] = $val['value'];
                    }
                }
            }
        }
        // load all custom fonts for preview
        if (is_customize_preview() || is_greyd_blocks()) {
            foreach (self::$cfontsInfo as $font) {
                // debug($font);
                // wp_enqueue_style($font['name'].'_font', $font['file']);
                self::add_font( $font['name'].'_font', $font['file'], array(), null );
            }
        }
        // enqueue custom font style
        else if (count($allcfonts) > 0) {
            $allcfonts = array_unique($allcfonts);
            for ($i = 0; $i < count($allcfonts); $i++) {
                if (self::$cfontsInfo[$allcfonts[$i]]) {
                    $name = self::$cfontsInfo[$allcfonts[$i]]['name'];
                    $file = self::$cfontsInfo[$allcfonts[$i]]['file'];
                    // wp_enqueue_style($name.'_font', $file);
                    self::add_font( $name.'_font', $file, array(), null );
                }
            }
        }
        // make google font style
        if (count($allgfonts) > 0) {
            $allgfonts = array_unique($allgfonts);
            // $fonts = "";

            // preconnect to google fonts
            echo '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />';

            for ($i = 0; $i < count($allgfonts); $i++) {
                $tmp = explode(':', $allgfonts[$i]);
                $font = str_replace(" ", "+", $tmp[0]);
                $params = "";
                if (count($tmp) > 1) {
                    $params = ":ital,wght@";
                    $tmp = explode(',', $tmp[1]);
                    $w = array(); $it = array();
                    for ($j = 0; $j<count($tmp); $j++) {
                        if (strpos($tmp[$j], 'italic') > -1)
                            array_push($it, str_replace('italic', '', $tmp[$j]));
                        else
                            array_push($w, $tmp[$j]);
                    }
                    if (count($w) > 0) {
                        for ($j = 0; $j<count($w); $j++) $w[$j] = "0,".$w[$j];
                        $params .= implode(';',$w);
                    }
                    if (count($it) > 0) {
                        if (count($w) > 0) $params .= ';';
                        for ($j = 0; $j<count($it); $j++) $it[$j] = "1,".$it[$j];
                        $params .= implode(';',$it);
                    }
                }
                // wp_enqueue_style('gfont_'.$font, 'https://fonts.googleapis.com/css2?family='.$font.$params, array(), null);
                self::add_font( 'gfont_'.$font, 'https://fonts.googleapis.com/css2?family='.$font.$params.'&display=swap', array(), null );
            }
        }
    }

    /**
     * Add font to @var $lazy_fonts or enqueue it directly
     * 
     * @param string $handle        Name of the stylesheet.
     * @param string $src           Full URL of the stylesheet.
     * @param string[] $deps        Dependant script handles.
     * @param string|bool|null $ver String specifying stylesheet version number.
     * @param string $media         The media for which this stylesheet has been defined.
     */
    public static function add_font( $handle, $src, $deps=array(), $ver=false, $media='all' ) {
        if ( \basics::get_setting(array('site', 'font_lazyload')) === 'true' ) {
            self::$lazy_fonts[ $handle ] = $ver ? add_query_arg('v', $ver, $src) : $src;
        }
        else {
            $crossorigin = strpos($handle, 'gfont_') === 0 ? 'crossorigin="anonymous"' : '';
            // preload font css
            echo '<link rel="preload" href="'.$src.'" as="style" '.$crossorigin.'>';
            echo '<link rel="stylesheet" id="'.$handle.'" href="'.$src.'" media="'.$media.'" '.$crossorigin.' />';
            // wp_enqueue_style( $handle, $src, $deps, $ver, $media );
        }
    }

    /**
     * Render all fonts from @var $lazy_fonts
     * 
     * @action 'wp_footer'
     */
    public static function enqueue_lazy_fonts() {
        if ( !count(self::$lazy_fonts) ) return;
        foreach( self::$lazy_fonts as $handle => $src ) {
            self::enqueue_lazy_font( $handle, $src );
        }
    }

    /**
     * Enqueue a font optimized for performance
     * 
     * @see https://css-tricks.com/how-to-load-fonts-in-a-way-that-fights-fout-and-makes-lighthouse-happy/
     */
    public static function enqueue_lazy_font( $handle, $src ) {
        echo sprintf(
            '<link rel="preload" as="style" href="%2$s" %3$s />'.
            '<link rel="stylesheet" id="%1$s" href="%2$s" media="print" onload="this.media=\'all\'" %3$s />'.
            '<noscript><link rel="stylesheet" id="%1$s" href="%2$s" /></noscript>',
            $handle,
            $src,
            strpos($handle, 'gfont_') === 0 ? 'crossorigin="anonymous"' : ''
        );
    }

    /*
        preprocess css files and add them inline
        support:
        - remove comments
        - :root
            - read :root { --variable: xx } 
            - translate to var(--variable)
            - replace to value
        - calc()
            - https://caniuse.com/#feat=calc
            - prefix calc(xx)
        - linear-gradient()
            - https://caniuse.com/#search=linear-gradient
            - prefix linear-gradient(xx);
        - flexbox
            - https://caniuse.com/#feat=flexbox
            - https://dev.opera.com/articles/advanced-cross-browser-flexbox/
            - prefix display: flex;
            - prefix display: inline-flex;
            - prefix justify-content: center;
            - prefix align-items: center;
            - prefix align-content: start;
        - border-radius
            - https://caniuse.com/#feat=border-radius
            - prefix porder-radius: xx;
        - box-shadow
            - https://caniuse.com/#feat=mdn-css_properties_box-shadow
            - prefix box-shadow: xx;
        - transition
            - https://caniuse.com/#feat=css-transitions
            - prefix transition: xx;
        - transform
            - https://caniuse.com/#search=transform
            - prefix transform: xx;
            - TODO
        - filter
            - https://caniuse.com/#feat=css-filters
            - webkit prefix filter: xx;
    */
    public static function process_styles($cssurl) {
        // get file contents as css string
        $css = \vc\helper::get_file_contents( $cssurl );
        $css = processor::process_styles_string($css);
        $css = apply_filters('greyd_process_styles', $css);
        return $css;
    }
    
    public static function process_styles_string($css) {
        if (count(processor::$mods) == 0) 
            self::process_defaults();

        //
        // remove comments
        //
        
        $tmp = explode("/*", $css);
        if (count($tmp) > 1) {
            $parts = array();
            $parts[] = $tmp[0];
            for ($i=1; $i<count($tmp); $i++) {
                $tmp1 = explode("*/", $tmp[$i]);
                //debug($tmp1[1]);
                //$parts[] = $tmp1[0];
                $parts[] = $tmp1[1];
            }
            $css = "";
            foreach ($parts as $part) $css .= $part;
        }

        // @media hack
        // debug(processor::$mods);
        $search = [
            "var(announcement_toggle)",
            "var(branding_toggle)",
            "var(grid_maxwidth)",
            "var(menu_toggle)",
            "var(meta_menu_toggle)",
            "min-width: var(navi_header_mobile_toggle)",
            "var(navi_header_mobile_toggle)",
            // dynamic grid
            "min-width: var(grid_sm)",
            "min-width: var(grid_md)",
            "min-width: var(grid_lg)",
            "min-width: var(grid_xl)",
            "max-width: var(grid_sm)",
            "max-width: var(grid_md)",
            "max-width: var(grid_lg)",
            "max-width: var(grid_xl)",
            "min-width: var(grid_wide)",
            "min-width: var(grid_normal)",
            "min-width: var(grid_narrow)",
            // vc grid
            "min-width: var(grid_vc_sm)",
            "min-width: var(grid_vc_md)",
            "min-width: var(grid_vc_lg)",
            "max-width: var(grid_vc_sm)",
            "max-width: var(grid_vc_md)",
            "max-width: var(grid_vc_lg)",
        ];
        $breakpoints = apply_filters('greyd_breakpoints', array(
            // legacy greyd breakpoints
            'sm' => 481, 'md' => 768, 'lg' => 1025, 'xl' => 1621,
            // legacy vc breakpoints
            'vc_sm' => 768, 'vc_md' => 992, 'vc_lg' => 1200
        ));
        $replace = [
            processor::$mods['announcement_toggle']."px",
            processor::$mods['branding_toggle']."px",
            processor::$mods['grid_maxwidth'],
            processor::$mods['menu_toggle']."px",
            processor::$mods['meta_menu_toggle']."px",
            "min-width: ".( ((int) processor::$mods['navi_header_mobile_toggle']) + 1 )."px",
            processor::$mods['navi_header_mobile_toggle']."px",
            // dynamic grid
            "min-width: ".$breakpoints['sm']."px",
            "min-width: ".$breakpoints['md']."px",
            "min-width: ".$breakpoints['lg']."px",
            "min-width: ".$breakpoints['xl']."px",
            "max-width: ".($breakpoints['sm']-0.02)."px",
            "max-width: ".($breakpoints['md']-0.02)."px",
            "max-width: ".($breakpoints['lg']-0.02)."px",
            "max-width: ".($breakpoints['xl']-0.02)."px",
            "min-width: ".(intval(processor::$mods['grid_maxwidth']) * (1+(2*processor::$mods['--GRDrowxxl'])))."px",
            "min-width: ".(intval(processor::$mods['grid_maxwidth']) * (1+(2*processor::$mods['--GRDrowxl'])))."px",
            "min-width: ".(intval(processor::$mods['grid_maxwidth']) * (1+(2*processor::$mods['--GRDrowl'])))."px",
            // vc grid
            "min-width: ".$breakpoints['vc_sm']."px",
            "min-width: ".$breakpoints['vc_md']."px",
            "min-width: ".$breakpoints['vc_lg']."px",
            "max-width: ".($breakpoints['vc_sm']-0.02)."px",
            "max-width: ".($breakpoints['vc_md']-0.02)."px",
            "max-width: ".($breakpoints['vc_lg']-0.02)."px",
        ];
        
        // // fontsize rem
        // for ($i=1; $i <= 6; $i++) {
        //     $search[] = 'var(--H'.$i.'fontSize-r)';
        //     $replace[] = str_replace('em', '', processor::$mods['--H'.$i.'fontSize'])."rem";
        // }
        
        // replace
        $css = str_replace( $search, $replace, $css );

        // fontfamily and color hacks
        // $css = str_replace("--XXfontFamily", "--".processor::$mods['--XXfontFamily'], $css);
        // $css = str_replace("--XXcolor", "--".processor::$mods['--XXcolor'], $css);
        foreach (processor::$mods as $var => $val) {
            if (!isset(processor::$root[$var])) continue;
            $root_value = processor::$root[$var];
            // fontfamilys
            if ($root_value['type'] == 'ffselect') {
                $css = str_replace("var(".$var.")", "var(--".$val.")", $css);
            }
            // colors
            else if ($root_value['type'] == 'colselect') {
                $css = str_replace("var(".$var.")", "var(--".$val.")", $css);
            }
        }

        //
        // get root vars from css string
        // add them to static $vars
        //
        $tmp = explode(":root", $css);
        //debug($tmp[1]);
        if (count($tmp) > 1) {
            $tmp1 = explode("{", $tmp[1]);
            //debug($tmp1[1]);
            $tmp2 = explode("}", $tmp1[1]);
            //debug($tmp2[0]);
            $pairs = explode(";", $tmp2[0]);
            foreach ($pairs as $key => $value) {
                //debug($key.":".$value);
                $pair = explode(":", $value);
                if (count($pair) > 1) {
                    $var = trim($pair[0]);
                    $val = trim($pair[1]);
                    //debug($var.":".$val);
                    processor::$vars[$var] = $val;
                }
            }            
        }
        //self::var_error_log(processor::$vars);
        //
        // handle root vars
        //
        if (is_customize_preview() || is_greyd_blocks()) {
            //
            // change root vars in css string
            //
            foreach (processor::$vars as $var => $val) {                
                if (isset(processor::$mods[$var]) && processor::$mods[$var] != null) {
                    // replace in css
                    $css = str_replace($var.": ".$val, $var.": ".processor::$mods[$var], $css);
                    // update var
                    processor::$vars[$var] = processor::$mods[$var];
                }
            }
        }
        else {
            //
            // replace Var(--variable) in css string
            //
            //debug(processor::$vars);
            foreach (processor::$vars as $var => $val) {
                if (isset(processor::$mods[$var]) && processor::$mods[$var] != null) 
                    $val = processor::$mods[$var];
                // replace in css
                $css = str_replace("var($var)", $val, $css);
                // update var
                processor::$vars[$var] = $val;
            }
            // remove root vars from css string
            $tmp = explode(":root", $css);
            if (count($tmp) > 1) {
                $parts = array();
                $parts[] = $tmp[0];
                for ($i=1; $i<count($tmp); $i++) {
                    $tmp1 = explode("}", $tmp[$i], 2);
                    //debug($tmp1[1]);
                    //$parts[] = $tmp1[0];
                    $parts[] = $tmp1[1];
                }
                $css = "";
                foreach ($parts as $part) $css .= $part;
            }
        }
        //
        // if customizer preview
        // return here and skip further processing of css string
        //
        if (is_customize_preview()) return $css;

        //
        // prefix css string
        //
        // prefix flexbox
        $css = str_replace(
            "display: flex;", 
            "display: -ms-Flexbox;
            -ms-box-orient: horizontal;
            -ms-box-pack: center;
            display: -webkit-flex;
            display: -moz-flex;
            display: -ms-flex;
            display: flex;",
            $css
        );
        $css = str_replace(
            "display: inline-flex;", 
            "display: -webkit-inline-box;
            display: -ms-inline-flexbox;
            display: inline-flex;",
            $css
        );
        $prefixes = array("-webkit-", "-moz-", "-ms-");
        $css = processor::prefix_style($css, "flex-direction", $prefixes);
        $css = processor::prefix_style($css, "justify-content", $prefixes);
        $css = processor::prefix_style($css, "align-items", $prefixes);
        $css = processor::prefix_style($css, "align-content", $prefixes);
        $css = processor::prefix_style($css, "align-self", $prefixes);

        // // prefix calc(xx)
        // $prefixes = array("-webkit-", "-moz-");
        // $css = processor::prefix_bracket_style($css, "calc", $prefixes);

        // prefix linear-gradient(xx)
        $prefixes = array("-webkit-", "-moz-", "-o-");
        $css = processor::prefix_bracket_style($css, "linear-gradient", $prefixes);

        // prefix border-radius
        $prefixes = array("-moz-", "-webkit-");
        $css = processor::prefix_style($css, "border-radius", $prefixes);

        // prefix box-shadow
        $prefixes = array("-moz-", "-webkit-");
        $css = processor::prefix_style($css, "box-shadow", $prefixes);

        // prefix transition
        $prefixes = array("-moz-", "-webkit-", "-o-");
        $css = processor::prefix_style($css, "transition", $prefixes);

        // // prefix transform
        // $prefixes = array("-moz-", "-webkit-", "-o-");
        // $css = processor::prefix_style($css, "transform", $prefixes);

        // prefix filter
        $prefixes = array("-webkit-");
        $css = processor::prefix_style($css, "filter", $prefixes);

        // prefix box-sizing
        $prefixes = array("-moz-", "-webkit-");
        $css = processor::prefix_style($css, "box-sizing", $prefixes);

        // trim
        $css = preg_replace('/\s+/S', " ", $css);

        return $css;
    }

    public static function prefix_style($css, $style, $pres=[]) {
        if (count($pres) == 0) $pres = array("-webkit-", "-moz-", "-ms-");
        $tmp = explode($style, $css);
        $hits = array();
        if (count($tmp) > 1) {
            for ($i=1; $i<count($tmp); $i++) {
                if (strpos($tmp[$i], ':') === false) continue;
                $tmp1 = explode(":", $tmp[$i]);
                $tmp2 = explode(";", $tmp1[1]);
                $hits[] = $tmp2[0];
            }          
        }
        $hits = array_unique($hits);
        foreach ($hits as $hit) {
            $old = $style.":".$hit.";";
            $new = "";
            foreach ($pres as $pre)
                $new .= $pre.$style.":".$hit.";";
            $new .= $old;
            $css = str_replace($old, $new, $css);
        }
        return $css;
    }

    public static function prefix_bracket_style($css, $style, $pres=[]) {
        if (count($pres) == 0) $pres = array("-webkit-", "-moz-", "-ms-");
        $tmp = explode($style, $css);
        $hits = array();
        if (count($tmp) > 1) {
            for ($i=1; $i<count($tmp); $i++) {
                // get value
                $tmp1 = explode(";", $tmp[$i]);
                $value = $style.$tmp1[0];
                // get key
                $tmp3 = explode(":", $tmp[$i-1]);
                $tmp4 = explode("{", $tmp3[count($tmp3)-2]);
                $tmp5 = explode(";", $tmp4[count($tmp4)-1]);
                $key = $tmp5[count($tmp5)-1];
                // make hit
                $hits[] = $key.": ".$value;
            }          
        }
        $hits = array_unique($hits);
        foreach ($hits as $hit) {
            $old = $hit.";";
            $new = "";
            foreach ($pres as $pre)
                $new .= str_replace($style, $pre.$style, $hit).";";
            $new .= $old;
            $css = str_replace($old, $new, $css);
        }
        return $css;
    }
    public static function var_error_log( $object=null ){
        ob_start();                    // start buffer capture
        var_dump( $object );           // dump the values
        $contents = ob_get_contents(); // put the buffer into a variable
        ob_end_clean();                // end capture
        error_log( $contents );        // log contents of the result of var_dump( $object )
    }
}
