From 0c1fa050f3021eaf7055627a732ddbaab20fd2f8 Mon Sep 17 00:00:00 2001
From: ale <ale@incal.net>
Date: Sat, 7 May 2022 12:02:25 +0100
Subject: [PATCH] Add a plugin to sanitize custom CSS data

---
 csstidy/class.csstidy-ctype.php    |   53 ++
 csstidy/class.csstidy-optimise.php | 1007 +++++++++++++++++++++
 csstidy/class.csstidy-print.php    |  432 +++++++++
 csstidy/class.csstidy.php          | 1307 ++++++++++++++++++++++++++++
 csstidy/cssparse-rtl.css           |  146 ++++
 csstidy/cssparse-rtl.min.css       |    1 +
 csstidy/cssparse.css               |  118 +++
 csstidy/cssparse.min.css           |    1 +
 csstidy/cssparsed-rtl.css          |   29 +
 csstidy/cssparsed-rtl.min.css      |    1 +
 csstidy/cssparsed.css              |   29 +
 csstidy/cssparsed.min.css          |    1 +
 csstidy/data-wp.inc.php            |  102 +++
 csstidy/data.inc.php               |  777 +++++++++++++++++
 csstidy/lang.inc.php               |  303 +++++++
 csstidy/wordpress-standard.tpl     |   10 +
 custom-css.php                     |   73 ++
 17 files changed, 4390 insertions(+)
 create mode 100644 csstidy/class.csstidy-ctype.php
 create mode 100644 csstidy/class.csstidy-optimise.php
 create mode 100644 csstidy/class.csstidy-print.php
 create mode 100644 csstidy/class.csstidy.php
 create mode 100644 csstidy/cssparse-rtl.css
 create mode 100644 csstidy/cssparse-rtl.min.css
 create mode 100644 csstidy/cssparse.css
 create mode 100644 csstidy/cssparse.min.css
 create mode 100644 csstidy/cssparsed-rtl.css
 create mode 100644 csstidy/cssparsed-rtl.min.css
 create mode 100644 csstidy/cssparsed.css
 create mode 100644 csstidy/cssparsed.min.css
 create mode 100644 csstidy/data-wp.inc.php
 create mode 100644 csstidy/data.inc.php
 create mode 100644 csstidy/lang.inc.php
 create mode 100644 csstidy/wordpress-standard.tpl
 create mode 100644 custom-css.php

diff --git a/csstidy/class.csstidy-ctype.php b/csstidy/class.csstidy-ctype.php
new file mode 100644
index 0000000..a94b39b
--- /dev/null
+++ b/csstidy/class.csstidy-ctype.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * CSSTidy - CSS Parser and Optimiser
+ *
+ * CSS ctype functions
+ * Defines some functions that can be not defined.
+ *
+ * This file is part of CSSTidy.
+ *
+ * CSSTidy is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * CSSTidy is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with CSSTidy; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @license https://opensource.org/licenses/gpl-license.php GNU Public License
+ * @package csstidy
+ * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
+ * @version 1.0
+ */
+
+if ( ! function_exists( 'ctype_space' ) ) {
+
+	/**
+	 * Check for whitespace character(s).
+	 *
+	 * @param string $text - the text.
+	 */
+	function ctype_space( $text ) {
+		return ! preg_match( "/[^\s\r\n\t\f]/", $text );
+	}
+}
+
+if ( ! function_exists( 'ctype_alpha' ) ) {
+
+	/**
+	 * Check for alphabetic character(s)
+	 *
+	 * @param string $text - the text.
+	 */
+	function ctype_alpha( $text ) {
+		return preg_match( '/[a-zA-Z]/', $text );
+	}
+}
+
diff --git a/csstidy/class.csstidy-optimise.php b/csstidy/class.csstidy-optimise.php
new file mode 100644
index 0000000..0889b01
--- /dev/null
+++ b/csstidy/class.csstidy-optimise.php
@@ -0,0 +1,1007 @@
+<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
+/**
+ * CSSTidy - CSS Parser and Optimiser
+ *
+ * CSS Optimising Class
+ * This class optimises CSS data generated by csstidy.
+ *
+ * Copyright 2005, 2006, 2007 Florian Schmitz
+ *
+ * This file is part of CSSTidy.
+ *
+ *   CSSTidy is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   CSSTidy is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * @license https://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
+ * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
+ */
+
+/**
+ * CSS Optimising Class
+ *
+ * This class optimises CSS data generated by csstidy.
+ *
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
+ * @version 1.0
+ */
+class csstidy_optimise { // phpcs:ignore
+	/**
+	 * Constructor
+	 *
+	 * @param array $css contains the class csstidy.
+	 * @access private
+	 * @version 1.0
+	 */
+	public function __construct( &$css ) {
+		$this->parser    = & $css;
+		$this->css       = & $css->css;
+		$this->sub_value = & $css->sub_value;
+		$this->at        = & $css->at;
+		$this->selector  = & $css->selector;
+		$this->property  = & $css->property;
+		$this->value     = & $css->value;
+	}
+
+	/**
+	 * Call constructor function.
+	 *
+	 * @param object $css - the CSS.
+	 */
+	public function csstidy_optimise( &$css ) {
+		$this->__construct( $css );
+	}
+
+	/**
+	 * Optimises $css after parsing
+	 *
+	 * @access public
+	 * @version 1.0
+	 */
+	public function postparse() {
+		if ( $this->parser->get_cfg( 'preserve_css' ) ) {
+			return;
+		}
+
+		if ( $this->parser->get_cfg( 'merge_selectors' ) === 2 ) {
+			foreach ( $this->css as $medium => $value ) {
+				$this->merge_selectors( $this->css[ $medium ] );
+			}
+		}
+
+		if ( $this->parser->get_cfg( 'discard_invalid_selectors' ) ) {
+			foreach ( $this->css as $medium => $value ) {
+				$this->discard_invalid_selectors( $this->css[ $medium ] );
+			}
+		}
+
+		if ( $this->parser->get_cfg( 'optimise_shorthands' ) > 0 ) {
+			foreach ( $this->css as $medium => $value ) {
+				foreach ( $value as $selector => $value1 ) {
+					$this->css[ $medium ][ $selector ] = self::merge_4value_shorthands( $this->css[ $medium ][ $selector ] );
+
+					if ( $this->parser->get_cfg( 'optimise_shorthands' ) < 2 ) {
+						continue;
+					}
+
+					$this->css[ $medium ][ $selector ] = self::merge_font( $this->css[ $medium ][ $selector ] );
+
+					if ( $this->parser->get_cfg( 'optimise_shorthands' ) < 3 ) {
+						continue;
+					}
+
+					$this->css[ $medium ][ $selector ] = self::merge_bg( $this->css[ $medium ][ $selector ] );
+					if ( empty( $this->css[ $medium ][ $selector ] ) ) {
+						unset( $this->css[ $medium ][ $selector ] );
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Optimises values
+	 *
+	 * @access public
+	 * @version 1.0
+	 */
+	public function value() {
+		$shorthands = & $GLOBALS['csstidy']['shorthands'];
+
+		// optimise shorthand properties.
+		if ( isset( $shorthands[ $this->property ] ) ) {
+			$temp = self::shorthand( $this->value ); // FIXME - move.
+			if ( $temp !== $this->value ) {
+				$this->parser->log( 'Optimised shorthand notation (' . $this->property . '): Changed "' . $this->value . '" to "' . $temp . '"', 'Information' );
+			}
+			$this->value = $temp;
+		}
+
+		// Remove whitespace at ! important.
+		if ( $this->value !== $this->compress_important( $this->value ) ) {
+			$this->parser->log( 'Optimised !important', 'Information' );
+		}
+	}
+
+	/**
+	 * Optimises shorthands
+	 *
+	 * @access public
+	 * @version 1.0
+	 */
+	public function shorthands() {
+		$shorthands = & $GLOBALS['csstidy']['shorthands'];
+
+		if ( ! $this->parser->get_cfg( 'optimise_shorthands' ) || $this->parser->get_cfg( 'preserve_css' ) ) {
+			return;
+		}
+
+		if ( $this->property === 'font' && $this->parser->get_cfg( 'optimise_shorthands' ) > 1 ) {
+			$this->css[ $this->at ][ $this->selector ]['font'] = '';
+			$this->parser->merge_css_blocks( $this->at, $this->selector, self::dissolve_short_font( $this->value ) );
+		}
+		if ( $this->property === 'background' && $this->parser->get_cfg( 'optimise_shorthands' ) > 2 ) {
+			$this->css[ $this->at ][ $this->selector ]['background'] = '';
+			$this->parser->merge_css_blocks( $this->at, $this->selector, self::dissolve_short_bg( $this->value ) );
+		}
+		if ( isset( $shorthands[ $this->property ] ) ) {
+			$this->parser->merge_css_blocks( $this->at, $this->selector, self::dissolve_4value_shorthands( $this->property, $this->value ) );
+			if ( is_array( $shorthands[ $this->property ] ) ) {
+				$this->css[ $this->at ][ $this->selector ][ $this->property ] = '';
+			}
+		}
+	}
+
+	/**
+	 * Optimises a sub-value
+	 *
+	 * @access public
+	 * @version 1.0
+	 */
+	public function subvalue() {
+		$replace_colors = & $GLOBALS['csstidy']['replace_colors'];
+
+		$this->sub_value = trim( $this->sub_value );
+		if ( $this->sub_value === '' ) {
+			return;
+		}
+
+		$important = '';
+		if ( csstidy::is_important( $this->sub_value ) ) {
+			$important = '!important';
+		}
+		$this->sub_value = csstidy::gvw_important( $this->sub_value );
+
+		// Compress font-weight.
+		if ( $this->property === 'font-weight' && $this->parser->get_cfg( 'compress_font-weight' ) ) {
+			if ( $this->sub_value === 'bold' ) {
+				$this->sub_value = '700';
+				$this->parser->log( 'Optimised font-weight: Changed "bold" to "700"', 'Information' );
+			} elseif ( $this->sub_value === 'normal' ) {
+				$this->sub_value = '400';
+				$this->parser->log( 'Optimised font-weight: Changed "normal" to "400"', 'Information' );
+			}
+		}
+
+		$temp = $this->compress_numbers( $this->sub_value );
+		if ( strcasecmp( $temp, $this->sub_value ) !== 0 ) {
+			if ( strlen( $temp ) > strlen( $this->sub_value ) ) {
+				$this->parser->log( 'Fixed invalid number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning' );
+			} else {
+				$this->parser->log( 'Optimised number: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information' );
+			}
+			$this->sub_value = $temp;
+		}
+		if ( $this->parser->get_cfg( 'compress_colors' ) ) {
+			$temp = $this->cut_color( $this->sub_value );
+			if ( $temp !== $this->sub_value ) {
+				if ( isset( $replace_colors[ $this->sub_value ] ) ) {
+					$this->parser->log( 'Fixed invalid color name: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Warning' );
+				} else {
+					$this->parser->log( 'Optimised color: Changed "' . $this->sub_value . '" to "' . $temp . '"', 'Information' );
+				}
+				$this->sub_value = $temp;
+			}
+		}
+		$this->sub_value .= $important;
+	}
+
+	/**
+	 * Compresses shorthand values. Example: margin:1px 1px 1px 1px -> margin:1px
+	 *
+	 * @param string $value - the value.
+	 * @access public
+	 * @return string
+	 * @version 1.0
+	 */
+	public static function shorthand( $value ) {
+		$important = '';
+		if ( csstidy::is_important( $value ) ) {
+			$values    = csstidy::gvw_important( $value );
+			$important = '!important';
+		} else {
+			$values = $value;
+		}
+
+		$values = explode( ' ', $values );
+		switch ( count( $values ) ) {
+			case 4:
+				if ( $values[0] === $values[1] && $values[0] === $values[2] && $values[0] === $values[3] ) {
+					return $values[0] . $important;
+				} elseif ( $values[1] === $values[3] && $values[0] === $values[2] ) {
+					return $values[0] . ' ' . $values[1] . $important;
+				} elseif ( $values[1] === $values[3] ) {
+					return $values[0] . ' ' . $values[1] . ' ' . $values[2] . $important;
+				}
+				break;
+
+			case 3:
+				if ( $values[0] === $values[1] && $values[0] === $values[2] ) {
+					return $values[0] . $important;
+				} elseif ( $values[0] === $values[2] ) {
+					return $values[0] . ' ' . $values[1] . $important;
+				}
+				break;
+
+			case 2:
+				if ( $values[0] === $values[1] ) {
+					return $values[0] . $important;
+				}
+				break;
+		}
+
+		return $value;
+	}
+
+	/**
+	 * Removes unnecessary whitespace in ! important
+	 *
+	 * @param string $string - the string.
+	 * @return string
+	 * @access public
+	 * @version 1.1
+	 */
+	public function compress_important( &$string ) {
+		if ( csstidy::is_important( $string ) ) {
+			$string = csstidy::gvw_important( $string ) . ' !important';      }
+		return $string;
+	}
+
+	/**
+	 * Color compression function. Converts all rgb() values to #-values and uses the short-form if possible. Also replaces 4 color names by #-values.
+	 *
+	 * @param string $color - the color.
+	 * @return string
+	 * @version 1.1
+	 */
+	public function cut_color( $color ) {
+		$replace_colors = & $GLOBALS['csstidy']['replace_colors'];
+
+		// an example: rgb(0,0,0) -> #000000 (or #000 in this case later).
+		if ( strtolower( substr( $color, 0, 4 ) ) === 'rgb(' ) {
+			$color_tmp = substr( $color, 4, strlen( $color ) - 5 );
+			$color_tmp = explode( ',', $color_tmp );
+			for ( $i = 0, $l = count( $color_tmp ); $i < $l; $i++ ) {
+				$color_tmp[ $i ] = trim( $color_tmp[ $i ] );
+				if ( substr( $color_tmp[ $i ], -1 ) === '%' ) {
+					$color_tmp[ $i ] = round( ( 255 * $color_tmp[ $i ] ) / 100 );
+				}
+				if ( $color_tmp[ $i ] > 255 ) {
+					$color_tmp[ $i ] = 255;
+				}
+			}
+			$color = '#';
+			for ( $i = 0; $i < 3; $i++ ) {
+				if ( $color_tmp[ $i ] < 16 ) {
+					$color .= '0' . dechex( $color_tmp[ $i ] );
+				} else {
+					$color .= dechex( $color_tmp[ $i ] );
+				}
+			}
+		}
+
+		// Fix bad color names.
+		if ( isset( $replace_colors[ strtolower( $color ) ] ) ) {
+			$color = $replace_colors[ strtolower( $color ) ];
+		}
+
+		// #aabbcc -> #abc
+		if ( strlen( $color ) === 7 ) {
+			$color_temp = strtolower( $color );
+			if ( $color_temp[0] === '#' && $color_temp[1] === $color_temp[2] && $color_temp[3] === $color_temp[4] && $color_temp[5] === $color_temp[6] ) {
+				$color = '#' . $color[1] . $color[3] . $color[5];
+			}
+		}
+
+		switch ( strtolower( $color ) ) {
+			/* color name -> hex code */
+			case 'black':
+				return '#000';
+			case 'fuchsia':
+				return '#f0f';
+			case 'white':
+				return '#fff';
+			case 'yellow':
+				return '#ff0';
+
+			/* hex code -> color name */
+			case '#800000':
+				return 'maroon';
+			case '#ffa500':
+				return 'orange';
+			case '#808000':
+				return 'olive';
+			case '#800080':
+				return 'purple';
+			case '#008000':
+				return 'green';
+			case '#000080':
+				return 'navy';
+			case '#008080':
+				return 'teal';
+			case '#c0c0c0':
+				return 'silver';
+			case '#808080':
+				return 'gray';
+			case '#f00':
+				return 'red';
+		}
+
+		return $color;
+	}
+
+	/**
+	 * Compresses numbers (ie. 1.0 becomes 1 or 1.100 becomes 1.1 )
+	 *
+	 * @param string $subvalue - the subvalue.
+	 * @return string
+	 * @version 1.2
+	 */
+	public function compress_numbers( $subvalue ) {
+		$unit_values  = & $GLOBALS['csstidy']['unit_values'];
+		$color_values = & $GLOBALS['csstidy']['color_values'];
+
+		// for font:1em/1em sans-serif...;.
+		if ( $this->property === 'font' ) {
+			$temp = explode( '/', $subvalue );
+		} else {
+			$temp = array( $subvalue );
+		}
+
+		for ( $l = 0, $m = count( $temp ); $l < $m; $l++ ) {
+			// if we are not dealing with a number at this point, do not optimise anything.
+			$number = $this->analyse_css_number( $temp[ $l ] );
+			if ( $number === false ) {
+				return $subvalue;
+			}
+
+			// Fix bad colors.
+			if ( in_array( $this->property, $color_values, true ) ) {
+				if ( strlen( $temp[ $l ] ) === 3 || strlen( $temp[ $l ] ) === 6 ) {
+					$temp[ $l ] = '#' . $temp[ $l ];
+				} else {
+					$temp[ $l ] = '0';
+				}
+				continue;
+			}
+
+			if ( abs( $number[0] ) > 0 ) {
+				if ( $number[1] === '' && in_array( $this->property, $unit_values, true ) ) {
+					$number[1] = 'px';
+				}
+			} else {
+				$number[1] = '';
+			}
+
+			$temp[ $l ] = $number[0] . $number[1];
+		}
+
+		return ( ( count( $temp ) > 1 ) ? $temp[0] . '/' . $temp[1] : $temp[0] );
+	}
+
+	/**
+	 * Checks if a given string is a CSS valid number. If it is,
+	 * an array containing the value and unit is returned
+	 *
+	 * @param string $string - the string we're checking.
+	 * @return array ('unit' if unit is found or '' if no unit exists, number value) or false if no number
+	 */
+	public function analyse_css_number( $string ) {
+		// most simple checks first
+		if ( $string === '' || ctype_alpha( $string[0] ) ) {
+			return false;
+		}
+
+		$units  = & $GLOBALS['csstidy']['units'];
+		$return = array( 0, '' );
+
+		$return[0] = (float) $string;
+		if ( abs( $return[0] ) > 0 && abs( $return[0] ) < 1 ) {
+			// Removes the initial `0` from a decimal number, e.g., `0.7 => .7` or `-0.666 => -.666`.
+			if ( ! $this->parser->get_cfg( 'preserve_leading_zeros' ) ) {
+				if ( $return[0] < 0 ) {
+					$return[0] = '-' . ltrim( substr( $return[0], 1 ), '0' );
+				} else {
+					$return[0] = ltrim( $return[0], '0' );
+				}
+			}
+		}
+
+		// Look for unit and split from value if exists
+		foreach ( $units as $unit ) {
+			$expect_unit_at = strlen( $string ) - strlen( $unit );
+			$unit_in_string = stristr( $string, $unit );
+			if ( ! $unit_in_string ) { // mb_strpos() fails with "false"
+				continue;
+			}
+			$actual_position = strpos( $string, $unit_in_string );
+			if ( $expect_unit_at === $actual_position ) {
+				$return[1] = $unit;
+				$string    = substr( $string, 0, - strlen( $unit ) );
+				break;
+			}
+		}
+		if ( ! is_numeric( $string ) ) {
+			return false;
+		}
+		return $return;
+	}
+
+	/**
+	 * Merges selectors with same properties. Example: a{color:red} b{color:red} -> a,b{color:red}
+	 * Very basic and has at least one bug. Hopefully there is a replacement soon.
+	 *
+	 * @param array $array - the selector array.
+	 * @access public
+	 * @version 1.2
+	 */
+	public function merge_selectors( &$array ) {
+		$css = $array;
+		foreach ( $css as $key => $value ) {
+			if ( ! isset( $css[ $key ] ) ) {
+				continue;
+			}
+			$newsel = '';
+
+			// Check if properties also exist in another selector.
+			$keys = array();
+			// PHP bug (?) without $css = $array; here.
+			foreach ( $css as $selector => $vali ) {
+				if ( $selector === $key ) {
+					continue;
+				}
+
+				if ( $css[ $key ] === $vali ) {
+					$keys[] = $selector;
+				}
+			}
+
+			if ( ! empty( $keys ) ) {
+				$newsel = $key;
+				unset( $css[ $key ] );
+				foreach ( $keys as $selector ) {
+					unset( $css[ $selector ] );
+					$newsel .= ',' . $selector;
+				}
+				$css[ $newsel ] = $value;
+			}
+		}
+		$array = $css;
+	}
+
+	/**
+	 * Removes invalid selectors and their corresponding rule-sets as
+	 * defined by 4.1.7 in REC-CSS2. This is a very rudimentary check
+	 * and should be replaced by a full-blown parsing algorithm or
+	 * regular expression
+	 *
+	 * @version 1.4
+	 *
+	 * @param array $array - selector array.
+	 */
+	public function discard_invalid_selectors( &$array ) {
+		foreach ( $array as $selector => $decls ) {
+			$ok        = true;
+			$selectors = array_map( 'trim', explode( ',', $selector ) );
+			foreach ( $selectors as $s ) {
+				$simple_selectors = preg_split( '/\s*[+>~\s]\s*/', $s );
+				foreach ( $simple_selectors as $ss ) {
+					if ( $ss === '' ) {
+						$ok = false;
+					}
+					// could also check $ss for internal structure, but that probably would be too slow.
+				}
+			}
+			if ( ! $ok ) {
+				unset( $array[ $selector ] );
+			}
+		}
+	}
+
+	/**
+	 * Dissolves properties like padding:10px 10px 10px to padding-top:10px;padding-bottom:10px;...
+	 *
+	 * @param string $property - the property.
+	 * @param string $value - the value.
+	 * @return array
+	 * @version 1.0
+	 * @see merge_4value_shorthands()
+	 */
+	public static function dissolve_4value_shorthands( $property, $value ) {
+		$shorthands = & $GLOBALS['csstidy']['shorthands'];
+		if ( ! is_array( $shorthands[ $property ] ) ) {
+			$return              = array();
+			$return[ $property ] = $value;
+			return $return;
+		}
+
+		$important = '';
+		if ( csstidy::is_important( $value ) ) {
+			$value     = csstidy::gvw_important( $value );
+			$important = '!important';
+		}
+		$values = explode( ' ', $value );
+
+		$return = array();
+		if ( count( $values ) === 4 ) {
+			for ( $i = 0; $i < 4; $i++ ) {
+				$return[ $shorthands[ $property ][ $i ] ] = $values[ $i ] . $important;
+			}
+		} elseif ( count( $values ) === 3 ) {
+			$return[ $shorthands[ $property ][0] ] = $values[0] . $important;
+			$return[ $shorthands[ $property ][1] ] = $values[1] . $important;
+			$return[ $shorthands[ $property ][3] ] = $values[1] . $important;
+			$return[ $shorthands[ $property ][2] ] = $values[2] . $important;
+		} elseif ( count( $values ) === 2 ) {
+			for ( $i = 0; $i < 4; $i++ ) {
+				$return[ $shorthands[ $property ][ $i ] ] = ( ( $i % 2 !== 0 ) ) ? $values[1] . $important : $values[0] . $important;
+			}
+		} else {
+			for ( $i = 0; $i < 4; $i++ ) {
+				$return[ $shorthands[ $property ][ $i ] ] = $values[0] . $important;
+			}
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Explodes a string as explode() does, however, not if $sep is escaped or within a string.
+	 *
+	 * @param string $sep - seperator.
+	 * @param string $string - the string.
+	 * @return array
+	 * @version 1.0
+	 */
+	public static function explode_ws( $sep, $string ) {
+		$status = 'st';
+		$to     = '';
+
+		$output = array();
+		$num    = 0;
+		for ( $i = 0, $len = strlen( $string ); $i < $len; $i++ ) {
+			switch ( $status ) {
+				case 'st':
+					if ( $string[ $i ] === $sep && ! csstidy::escaped( $string, $i ) ) {
+						++$num;
+					} elseif ( $string[ $i ] === '"' || $string[ $i ] === '\'' || $string[ $i ] === '(' && ! csstidy::escaped( $string, $i ) ) {
+						$status = 'str';
+						$to     = ( $string[ $i ] === '(' ) ? ')' : $string[ $i ];
+						( isset( $output[ $num ] ) ) ? $output[ $num ] .= $string[ $i ] : $output[ $num ] = $string[ $i ];
+					} else {
+						( isset( $output[ $num ] ) ) ? $output[ $num ] .= $string[ $i ] : $output[ $num ] = $string[ $i ];
+					}
+					break;
+
+				case 'str':
+					if ( $string[ $i ] === $to && ! csstidy::escaped( $string, $i ) ) {
+						$status = 'st';
+					}
+					( isset( $output[ $num ] ) ) ? $output[ $num ] .= $string[ $i ] : $output[ $num ] = $string[ $i ];
+					break;
+			}
+		}
+
+		if ( isset( $output[0] ) ) {
+			return $output;
+		} else {
+			return array( $output );
+		}
+	}
+
+	/**
+	 * Merges Shorthand properties again, the opposite of dissolve_4value_shorthands()
+	 *
+	 * @param array $array - the property array.
+	 * @return array
+	 * @version 1.2
+	 * @see dissolve_4value_shorthands()
+	 */
+	public static function merge_4value_shorthands( $array ) {
+		$return     = $array;
+		$shorthands = & $GLOBALS['csstidy']['shorthands'];
+
+		foreach ( $shorthands as $key => $value ) {
+			if ( isset( $array[ $value[0] ] ) && isset( $array[ $value[1] ] )
+							&& isset( $array[ $value[2] ] ) && isset( $array[ $value[3] ] ) && $value !== 0 ) {
+				$return[ $key ] = '';
+
+				$important = '';
+				for ( $i = 0; $i < 4; $i++ ) {
+					$val = $array[ $value[ $i ] ];
+					if ( csstidy::is_important( $val ) ) {
+						$important       = '!important';
+						$return[ $key ] .= csstidy::gvw_important( $val ) . ' ';
+					} else {
+						$return[ $key ] .= $val . ' ';
+					}
+					unset( $return[ $value[ $i ] ] );
+				}
+				$return[ $key ] = self::shorthand( trim( $return[ $key ] . $important ) );
+			}
+		}
+		return $return;
+	}
+
+	/**
+	 * Dissolve background property
+	 *
+	 * @param string $str_value - the string value.
+	 * @return array
+	 * @version 1.0
+	 * @see merge_bg()
+	 * @todo full CSS 3 compliance
+	 */
+	public static function dissolve_short_bg( $str_value ) {
+		$have = array();
+		// don't try to explose background gradient !
+		if ( stripos( $str_value, 'gradient(' ) !== false ) {
+			return array( 'background' => $str_value );
+		}
+
+		$background_prop_default = & $GLOBALS['csstidy']['background_prop_default'];
+		$repeat                  = array( 'repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'space' );
+		$attachment              = array( 'scroll', 'fixed', 'local' );
+		$clip                    = array( 'border', 'padding' );
+		$origin                  = array( 'border', 'padding', 'content' );
+		$pos                     = array( 'top', 'center', 'bottom', 'left', 'right' );
+		$important               = '';
+		$return                  = array(
+			'background-image'      => null,
+			'background-size'       => null,
+			'background-repeat'     => null,
+			'background-position'   => null,
+			'background-attachment' => null,
+			'background-clip'       => null,
+			'background-origin'     => null,
+			'background-color'      => null,
+		);
+
+		if ( csstidy::is_important( $str_value ) ) {
+			$important = ' !important';
+			$str_value = csstidy::gvw_important( $str_value );
+		}
+
+		$str_value = self::explode_ws( ',', $str_value );
+		for ( $i = 0, $l = count( $str_value ); $i < $l; $i++ ) {
+			$have['clip']  = false;
+			$have['pos']   = false;
+			$have['color'] = false;
+			$have['bg']    = false;
+
+			if ( is_array( $str_value[ $i ] ) ) {
+				$str_value[ $i ] = $str_value[ $i ][0];
+			}
+			$str_value[ $i ] = self::explode_ws( ' ', trim( $str_value[ $i ] ) );
+
+			for ( $j = 0, $k = count( $str_value[ $i ] ); $j < $k; $j++ ) {
+				if ( $have['bg'] === false && ( substr( $str_value[ $i ][ $j ], 0, 4 ) === 'url(' || $str_value[ $i ][ $j ] === 'none' ) ) {
+					$return['background-image'] .= $str_value[ $i ][ $j ] . ',';
+					$have['bg']                  = true;
+				} elseif ( in_array( $str_value[ $i ][ $j ], $repeat, true ) ) {
+					$return['background-repeat'] .= $str_value[ $i ][ $j ] . ',';
+				} elseif ( in_array( $str_value[ $i ][ $j ], $attachment, true ) ) {
+					$return['background-attachment'] .= $str_value[ $i ][ $j ] . ',';
+				} elseif ( in_array( $str_value[ $i ][ $j ], $clip, true ) && ! $have['clip'] ) {
+					$return['background-clip'] .= $str_value[ $i ][ $j ] . ',';
+					$have['clip']               = true;
+				} elseif ( in_array( $str_value[ $i ][ $j ], $origin, true ) ) {
+					$return['background-origin'] .= $str_value[ $i ][ $j ] . ',';
+				} elseif ( $str_value[ $i ][ $j ][0] === '(' ) {
+					$return['background-size'] .= substr( $str_value[ $i ][ $j ], 1, -1 ) . ',';
+				} elseif ( in_array( $str_value[ $i ][ $j ], $pos, true ) || is_numeric( $str_value[ $i ][ $j ][0] ) || $str_value[ $i ][ $j ][0] === null || $str_value[ $i ][ $j ][0] === '-' || $str_value[ $i ][ $j ][0] === '.' ) {
+					$return['background-position'] .= $str_value[ $i ][ $j ];
+					if ( ! $have['pos'] ) {
+						$return['background-position'] .= ' ';
+					} else {
+						$return['background-position'] .= ',';
+					}
+					$have['pos'] = true;
+				} elseif ( ! $have['color'] ) {
+					$return['background-color'] .= $str_value[ $i ][ $j ] . ',';
+					$have['color']               = true;
+				}
+			}
+		}
+
+		foreach ( $background_prop_default as $bg_prop => $default_value ) {
+			if ( $return[ $bg_prop ] !== null ) {
+				$return[ $bg_prop ] = substr( $return[ $bg_prop ], 0, -1 ) . $important;
+			} else {
+				$return[ $bg_prop ] = $default_value . $important;
+			}
+		}
+		return $return;
+	}
+
+	/**
+	 * Merges all background properties
+	 *
+	 * @param array $input_css - inputted CSS.
+	 * @return array
+	 * @version 1.0
+	 * @see dissolve_short_bg()
+	 * @todo full CSS 3 compliance
+	 */
+	public static function merge_bg( $input_css ) {
+		$background_prop_default = & $GLOBALS['csstidy']['background_prop_default'];
+		// Max number of background images. CSS3 not yet fully implemented.
+		$number_of_values = @max( count( self::explode_ws( ',', $input_css['background-image'] ) ), count( self::explode_ws( ',', $input_css['background-color'] ) ), 1 ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
+		// Array with background images to check if BG image exists.
+		$bg_img_array = @self::explode_ws( ',', csstidy::gvw_important( $input_css['background-image'] ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
+		$new_bg_value = '';
+		$important    = '';
+
+		// if background properties is here and not empty, don't try anything.
+		if ( isset( $input_css['background'] ) && $input_css['background'] ) {
+			return $input_css;
+		}
+
+		for ( $i = 0; $i < $number_of_values; $i++ ) {
+			foreach ( $background_prop_default as $bg_property => $default_value ) {
+				// Skip if property does not exist
+				if ( ! isset( $input_css[ $bg_property ] ) ) {
+					continue;
+				}
+
+				$cur_value = $input_css[ $bg_property ];
+				// skip all optimisation if gradient() somewhere.
+				if ( stripos( $cur_value, 'gradient(' ) !== false ) {
+					return $input_css;
+				}
+
+				// Skip some properties if there is no background image.
+				if ( ( ! isset( $bg_img_array[ $i ] ) || $bg_img_array[ $i ] === 'none' )
+								&& ( $bg_property === 'background-size' || $bg_property === 'background-position'
+								|| $bg_property === 'background-attachment' || $bg_property === 'background-repeat' ) ) {
+					continue;
+				}
+
+				// Remove !important.
+				if ( csstidy::is_important( $cur_value ) ) {
+					$important = ' !important';
+					$cur_value = csstidy::gvw_important( $cur_value );
+				}
+
+				// Do not add default values.
+				if ( $cur_value === $default_value ) {
+					continue;
+				}
+
+				$temp = self::explode_ws( ',', $cur_value );
+
+				if ( isset( $temp[ $i ] ) ) {
+					if ( $bg_property === 'background-size' ) {
+						$new_bg_value .= '(' . $temp[ $i ] . ') ';
+					} else {
+						$new_bg_value .= $temp[ $i ] . ' ';
+					}
+				}
+			}
+
+			$new_bg_value = trim( $new_bg_value );
+			if ( $i !== $number_of_values - 1 ) {
+				$new_bg_value .= ',';
+			}
+		}
+
+		// Delete all background-properties.
+		foreach ( $background_prop_default as $bg_property => $default_value ) {
+			unset( $input_css[ $bg_property ] );
+		}
+
+		// Add new background property.
+		if ( $new_bg_value !== '' ) {
+			$input_css['background'] = $new_bg_value . $important;
+		} elseif ( isset( $input_css['background'] ) ) {
+			$input_css['background'] = 'none';
+		}
+
+		return $input_css;
+	}
+
+	/**
+	 * Dissolve font property
+	 *
+	 * @param string $str_value - the string value.
+	 * @return array
+	 * @version 1.3
+	 * @see merge_font()
+	 */
+	public static function dissolve_short_font( $str_value ) {
+		$have              = array();
+		$font_prop_default = & $GLOBALS['csstidy']['font_prop_default'];
+		$font_weight       = array( 'normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900' );
+		$font_variant      = array( 'normal', 'small-caps' );
+		$font_style        = array( 'normal', 'italic', 'oblique' );
+		$important         = '';
+		$return            = array(
+			'font-style'   => null,
+			'font-variant' => null,
+			'font-weight'  => null,
+			'font-size'    => null,
+			'line-height'  => null,
+			'font-family'  => null,
+		);
+
+		if ( csstidy::is_important( $str_value ) ) {
+			$important = '!important';
+			$str_value = csstidy::gvw_important( $str_value );
+		}
+
+		$have['style']   = false;
+		$have['variant'] = false;
+		$have['weight']  = false;
+		$have['size']    = false;
+		// Detects if font-family consists of several words w/o quotes.
+		$multiwords = false;
+
+		// Workaround with multiple font-family.
+		$str_value = self::explode_ws( ',', trim( $str_value ) );
+
+		$str_value[0] = self::explode_ws( ' ', trim( $str_value[0] ) );
+
+		for ( $j = 0, $k = count( $str_value[0] ); $j < $k; $j++ ) {
+			if ( $have['weight'] === false && in_array( $str_value[0][ $j ], $font_weight, true ) ) {
+				$return['font-weight'] = $str_value[0][ $j ];
+				$have['weight']        = true;
+			} elseif ( $have['variant'] === false && in_array( $str_value[0][ $j ], $font_variant, true ) ) {
+				$return['font-variant'] = $str_value[0][ $j ];
+				$have['variant']        = true;
+			} elseif ( $have['style'] === false && in_array( $str_value[0][ $j ], $font_style, true ) ) {
+				$return['font-style'] = $str_value[0][ $j ];
+				$have['style']        = true;
+			} elseif ( $have['size'] === false && ( is_numeric( $str_value[0][ $j ][0] ) || $str_value[0][ $j ][0] === null || $str_value[0][ $j ][0] === '.' ) ) {
+				$size                = self::explode_ws( '/', trim( $str_value[0][ $j ] ) );
+				$return['font-size'] = $size[0];
+				if ( isset( $size[1] ) ) {
+					$return['line-height'] = $size[1];
+				} else {
+					$return['line-height'] = ''; // don't add 'normal' !
+				}
+				$have['size'] = true;
+			} else {
+				if ( isset( $return['font-family'] ) ) {
+					$return['font-family'] .= ' ' . $str_value[0][ $j ];
+					$multiwords             = true;
+				} else {
+					$return['font-family'] = $str_value[0][ $j ];
+				}
+			}
+		}
+		// add quotes if we have several qords in font-family.
+		if ( $multiwords !== false ) {
+			$return['font-family'] = '"' . $return['font-family'] . '"';
+		}
+		$i = 1;
+		while ( isset( $str_value[ $i ] ) ) {
+			$return['font-family'] .= ',' . trim( $str_value[ $i ] );
+			$i++;
+		}
+
+		// Fix for 100 and more font-size.
+		if ( $have['size'] === false && isset( $return['font-weight'] ) &&
+			is_numeric( $return['font-weight'][0] )
+		) {
+			$return['font-size'] = $return['font-weight'];
+			unset( $return['font-weight'] );
+		}
+
+		foreach ( $font_prop_default as $font_prop => $default_value ) {
+			if ( $return[ $font_prop ] !== null ) {
+				$return[ $font_prop ] = $return[ $font_prop ] . $important;
+			} else {
+				$return[ $font_prop ] = $default_value . $important;
+			}
+		}
+		return $return;
+	}
+
+	/**
+	 * Merges all fonts properties
+	 *
+	 * @param array $input_css - input CSS.
+	 * @return array
+	 * @version 1.3
+	 * @see dissolve_short_font()
+	 */
+	public static function merge_font( $input_css ) {
+		$font_prop_default = & $GLOBALS['csstidy']['font_prop_default'];
+		$new_font_value    = '';
+		$important         = '';
+		// Skip if not font-family and font-size set.
+		if ( isset( $input_css['font-family'] ) && isset( $input_css['font-size'] ) ) {
+			// fix several words in font-family - add quotes.
+			if ( isset( $input_css['font-family'] ) ) {
+				$families        = explode( ',', $input_css['font-family'] );
+				$result_families = array();
+				foreach ( $families as $family ) {
+					$family = trim( $family );
+					$len    = strlen( $family );
+					if ( strpos( $family, ' ' ) &&
+									! ( ( $family[0] === '"' && $family[ $len - 1 ] === '"' ) ||
+									( $family[0] === "'" && $family[ $len - 1 ] === "'" ) ) ) {
+						$family = '"' . $family . '"';
+					}
+					$result_families[] = $family;
+				}
+				$input_css['font-family'] = implode( ',', $result_families );
+			}
+			foreach ( $font_prop_default as $font_property => $default_value ) {
+
+				// Skip if property does not exist.
+				if ( ! isset( $input_css[ $font_property ] ) ) {
+					continue;
+				}
+
+				$cur_value = $input_css[ $font_property ];
+
+				// Skip if default value is used.
+				if ( $cur_value === $default_value ) {
+					continue;
+				}
+
+				// Remove !important.
+				if ( csstidy::is_important( $cur_value ) ) {
+					$important = '!important';
+					$cur_value = csstidy::gvw_important( $cur_value );
+				}
+
+				$new_font_value .= $cur_value;
+				// Add delimiter.
+				$new_font_value .= ( $font_property === 'font-size' &&
+								isset( $input_css['line-height'] ) ) ? '/' : ' ';
+			}
+
+			$new_font_value = trim( $new_font_value );
+
+			// Delete all font-properties.
+			foreach ( $font_prop_default as $font_property => $default_value ) {
+				if ( $font_property !== 'font' || ! $new_font_value ) {
+					unset( $input_css[ $font_property ] );
+				}
+			}
+
+			// Add new font property.
+			if ( $new_font_value !== '' ) {
+				$input_css['font'] = $new_font_value . $important;
+			}
+		}
+
+		return $input_css;
+	}
+
+}
diff --git a/csstidy/class.csstidy-print.php b/csstidy/class.csstidy-print.php
new file mode 100644
index 0000000..5ce9de5
--- /dev/null
+++ b/csstidy/class.csstidy-print.php
@@ -0,0 +1,432 @@
+<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
+/**
+ * CSSTidy - CSS Parser and Optimiser
+ *
+ * CSS Printing class
+ * This class prints CSS data generated by csstidy.
+ *
+ * Copyright 2005, 2006, 2007 Florian Schmitz
+ *
+ * This file is part of CSSTidy.
+ *
+ *   CSSTidy is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   CSSTidy is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * @license https://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
+ * @author Cedric Morin (cedric at yterium dot com) 2010
+ */
+
+/**
+ * CSS Printing class
+ *
+ * This class prints CSS data generated by csstidy.
+ *
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
+ * @version 1.0.1
+ */
+class csstidy_print { // phpcs:ignore
+
+	/**
+	 * Saves the input CSS string
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $input_css = '';
+	/**
+	 * Saves the formatted CSS string
+	 *
+	 * @var string
+	 * @access public
+	 */
+	public $output_css = '';
+	/**
+	 * Saves the formatted CSS string (plain text)
+	 *
+	 * @var string
+	 * @access public
+	 */
+	public $output_css_plain = '';
+
+	/**
+	 * Constructor
+	 *
+	 * @param array $css contains the class csstidy.
+	 * @access private
+	 * @version 1.0
+	 */
+	public function __construct( &$css ) {
+		$this->parser    = & $css;
+		$this->css       = & $css->css;
+		$this->template  = & $css->template;
+		$this->tokens    = & $css->tokens;
+		$this->charset   = & $css->charset;
+		$this->import    = & $css->import;
+		$this->namespace = & $css->namespace;
+	}
+
+	/**
+	 * Call constructor function.
+	 *
+	 * @param object $css - the CSS we're working with.
+	 */
+	public function csstidy_print( &$css ) {
+		$this->__construct( $css );
+	}
+
+	/**
+	 * Resets output_css and output_css_plain (new css code)
+	 *
+	 * @access private
+	 * @version 1.0
+	 */
+	public function _reset() { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
+		$this->output_css       = '';
+		$this->output_css_plain = '';
+	}
+
+	/**
+	 * Returns the CSS code as plain text
+	 *
+	 * @param string $default_media default @media to add to selectors without any @media.
+	 * @return string
+	 * @access public
+	 * @version 1.0
+	 */
+	public function plain( $default_media = '' ) {
+		$this->_print( true, $default_media );
+		return $this->output_css_plain;
+	}
+
+	/**
+	 * Returns the formatted CSS code
+	 *
+	 * @param string $default_media default @media to add to selectors without any @media.
+	 * @return string
+	 * @access public
+	 * @version 1.0
+	 */
+	public function formatted( $default_media = '' ) {
+		$this->_print( false, $default_media );
+		return $this->output_css;
+	}
+
+	/**
+	 * Returns the formatted CSS code to make a complete webpage
+	 *
+	 * @param string $doctype shorthand for the document type.
+	 * @param bool   $externalcss indicates whether styles to be attached internally or as an external stylesheet.
+	 * @param string $title title to be added in the head of the document.
+	 * @param string $lang two-letter language code to be added to the output.
+	 * @return string
+	 * @access public
+	 * @version 1.4
+	 */
+	public function formatted_page( $doctype = 'xhtml1.1', $externalcss = true, $title = '', $lang = 'en' ) {
+		switch ( $doctype ) {
+			case 'xhtml1.0strict':
+				$doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+			"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
+				break;
+			case 'xhtml1.1':
+			default:
+				$doctype_output = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+				"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
+				break;
+		}
+		$cssparsed              = '';
+		$output                 = '';
+		$this->output_css_plain = & $output;
+
+		$output .= $doctype_output . "\n" . '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="' . $lang . '"';
+		$output .= ( $doctype === 'xhtml1.1' ) ? '>' : ' lang="' . $lang . '">';
+		$output .= "\n<head>\n    <title>$title</title>";
+
+		if ( $externalcss ) {
+			$output   .= "\n    <style type=\"text/css\">\n";
+			$cssparsed = file_get_contents( 'cssparsed.css' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
+			$output   .= $cssparsed; // Adds an invisible BOM or something, but not in css_optimised.php
+			$output   .= "\n</style>";
+		} else {
+			$output .= "\n" . '    <link rel="stylesheet" type="text/css" href="cssparsed.css" />'; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
+			// }
+		}
+		$output .= "\n</head>\n<body><code id=\"copytext\">";
+		$output .= $this->formatted();
+		$output .= '</code>' . "\n" . '</body></html>';
+		return $this->output_css_plain;
+	}
+
+	/**
+	 * Returns the formatted CSS Code and saves it into $this->output_css and $this->output_css_plain
+	 *
+	 * @param bool   $plain plain text or not.
+	 * @param string $default_media default @media to add to selectors without any @media.
+	 * @access private
+	 * @version 2.0
+	 */
+	public function _print( $plain = false, $default_media = '' ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore -- print is a reserved word anyway.
+		if ( $this->output_css && $this->output_css_plain ) {
+			return;
+		}
+
+		$output = '';
+		if ( ! $this->parser->get_cfg( 'preserve_css' ) ) {
+			$this->convert_raw_css( $default_media );
+		}
+
+		$template = & $this->template;
+
+		if ( $plain ) {
+			$template = array_map( 'strip_tags', $template );
+		}
+
+		if ( $this->parser->get_cfg( 'timestamp' ) ) {
+			array_unshift( $this->tokens, array( COMMENT, ' CSSTidy ' . $this->parser->version . ': ' . gmdate( 'r' ) . ' ' ) );
+		}
+
+		if ( ! empty( $this->charset ) ) {
+			$output .= $template[0] . '@charset ' . $template[5] . $this->charset . $template[6];
+		}
+
+		if ( ! empty( $this->import ) ) {
+			for ( $i = 0, $size = count( $this->import ); $i < $size; $i++ ) {
+				$import_components = explode( ' ', $this->import[ $i ] );
+				if ( substr( $import_components[0], 0, 4 ) === 'url(' && substr( $import_components[0], -1, 1 ) === ')' ) {
+					$import_components[0] = '\'' . trim( substr( $import_components[0], 4, -1 ), "'\"" ) . '\'';
+					$this->import[ $i ]   = implode( ' ', $import_components );
+					$this->parser->log( 'Optimised @import : Removed "url("', 'Information' );
+				}
+				$output .= $template[0] . '@import ' . $template[5] . $this->import[ $i ] . $template[6];
+			}
+		}
+		if ( ! empty( $this->namespace ) ) {
+			if ( substr( $this->namespace, 0, 4 ) === 'url(' && substr( $this->namespace, -1, 1 ) === ')' ) {
+				$this->namespace = '\'' . substr( $this->namespace, 4, -1 ) . '\'';
+				$this->parser->log( 'Optimised @namespace : Removed "url("', 'Information' );
+			}
+			$output .= $template[0] . '@namespace ' . $template[5] . $this->namespace . $template[6];
+		}
+
+		$output   .= $template[13];
+		$in_at_out = '';
+		$out       = & $output;
+
+		foreach ( $this->tokens as $key => $token ) {
+			switch ( $token[0] ) {
+				case AT_START:
+					$out .= $template[0] . $this->htmlsp( $token[1], $plain ) . $template[1];
+					$out  = & $in_at_out;
+					break;
+
+				case SEL_START:
+					if ( $this->parser->get_cfg( 'lowercase_s' ) ) {
+						$token[1] = strtolower( $token[1] );
+					}
+					$out .= ( $token[1][0] !== '@' ) ? $template[2] . $this->htmlsp( $token[1], $plain ) : $template[0] . $this->htmlsp( $token[1], $plain );
+					$out .= $template[3];
+					break;
+
+				case PROPERTY:
+					if ( $this->parser->get_cfg( 'case_properties' ) === 2 ) {
+						$token[1] = strtoupper( $token[1] );
+					} elseif ( $this->parser->get_cfg( 'case_properties' ) === 1 ) {
+						$token[1] = strtolower( $token[1] );
+					}
+					$out .= $template[4] . $this->htmlsp( $token[1], $plain ) . ':' . $template[5];
+					break;
+
+				case VALUE:
+					$out .= $this->htmlsp( $token[1], $plain );
+					if ( $this->seeknocomment( $key, 1 ) === SEL_END && $this->parser->get_cfg( 'remove_last_;' ) ) {
+						$out .= str_replace( ';', '', $template[6] );
+					} else {
+						$out .= $template[6];
+					}
+					break;
+
+				case SEL_END:
+					$out .= $template[7];
+					if ( $this->seeknocomment( $key, 1 ) !== AT_END ) {
+						$out .= $template[8];
+					}
+					break;
+
+				case AT_END:
+					$out       = & $output;
+					$out      .= $template[10] . str_replace( "\n", "\n" . $template[10], $in_at_out );
+					$in_at_out = '';
+					$out      .= $template[9];
+					break;
+
+				case COMMENT:
+					$out .= $template[11] . '/*' . $this->htmlsp( $token[1], $plain ) . '*/' . $template[12];
+					break;
+			}
+		}
+
+		$output = trim( $output );
+
+		if ( ! $plain ) {
+			$this->output_css = $output;
+			$this->_print( true );
+		} else {
+			// If using spaces in the template, don't want these to appear in the plain output
+			$this->output_css_plain = str_replace( '&#160;', '', $output );
+		}
+	}
+
+	/**
+	 * Gets the next token type which is $move away from $key, excluding comments
+	 *
+	 * @param integer $key current position.
+	 * @param integer $move move this far.
+	 * @return mixed a token type
+	 * @access private
+	 * @version 1.0
+	 */
+	public function seeknocomment( $key, $move ) {
+		$go = ( $move > 0 ) ? 1 : -1;
+		for ( $i = $key + 1; abs( $key - $i ) - 1 < abs( $move ); $i += $go ) { // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
+			if ( ! isset( $this->tokens[ $i ] ) ) {
+				return;
+			}
+			if ( $this->tokens[ $i ][0] === COMMENT ) {
+				++$move;
+				continue;
+			}
+			return $this->tokens[ $i ][0];
+		}
+	}
+
+	/**
+	 * Converts $this->css array to a raw array ($this->tokens)
+	 *
+	 * @param string $default_media default @media to add to selectors without any @media.
+	 * @access private
+	 * @version 1.0
+	 */
+	public function convert_raw_css( $default_media = '' ) {
+		$this->tokens = array();
+
+		foreach ( $this->css as $medium => $val ) {
+			if ( $this->parser->get_cfg( 'sort_selectors' ) ) {
+				ksort( $val );
+			}
+			if ( (int) $medium < DEFAULT_AT ) {
+				$this->parser->_add_token( AT_START, $medium, true );
+			} elseif ( $default_media ) {
+				$this->parser->_add_token( AT_START, $default_media, true );
+			}
+
+			foreach ( $val as $selector => $vali ) {
+				if ( $this->parser->get_cfg( 'sort_properties' ) ) {
+					ksort( $vali );
+				}
+				$this->parser->_add_token( SEL_START, $selector, true );
+
+				foreach ( $vali as $property => $valj ) {
+					$this->parser->_add_token( PROPERTY, $property, true );
+					$this->parser->_add_token( VALUE, $valj, true );
+				}
+
+				$this->parser->_add_token( SEL_END, $selector, true );
+			}
+
+			if ( (int) $medium < DEFAULT_AT ) {
+				$this->parser->_add_token( AT_END, $medium, true );
+			} elseif ( $default_media ) {
+				$this->parser->_add_token( AT_END, $default_media, true );
+			}
+		}
+	}
+
+	/**
+	 * Same as htmlspecialchars, only that chars are not replaced if $plain !== true. This makes  print_code() cleaner.
+	 *
+	 * @param string $string - the string we're converting.
+	 * @param bool   $plain - plain text or not.
+	 * @return string
+	 * @see csstidy_print::_print()
+	 * @access private
+	 * @version 1.0
+	 */
+	public function htmlsp( $string, $plain ) {
+		if ( ! $plain ) {
+			return htmlspecialchars( $string, ENT_QUOTES, 'utf-8' );
+		}
+		return $string;
+	}
+
+	/**
+	 * Get compression ratio
+	 *
+	 * @access public
+	 * @return float
+	 * @version 1.2
+	 */
+	public function get_ratio() {
+		if ( ! $this->output_css_plain ) {
+			$this->formatted();
+		}
+		return round( ( strlen( $this->input_css ) - strlen( $this->output_css_plain ) ) / strlen( $this->input_css ), 3 ) * 100;
+	}
+
+	/**
+	 * Get difference between the old and new code in bytes and prints the code if necessary.
+	 *
+	 * @access public
+	 * @return string
+	 * @version 1.1
+	 */
+	public function get_diff() {
+		if ( ! $this->output_css_plain ) {
+			$this->formatted();
+		}
+
+		$diff = strlen( $this->output_css_plain ) - strlen( $this->input_css );
+
+		if ( $diff > 0 ) {
+			return '+' . $diff;
+		} elseif ( $diff === 0 ) {
+			return '+-' . $diff;
+		}
+
+		return $diff;
+	}
+
+	/**
+	 * Get the size of either input or output CSS in KB
+	 *
+	 * @param string $loc default is "output".
+	 * @access public
+	 * @return integer
+	 * @version 1.0
+	 */
+	public function size( $loc = 'output' ) {
+		if ( $loc === 'output' && ! $this->output_css ) {
+			$this->formatted();
+		}
+
+		if ( $loc === 'input' ) {
+			return ( strlen( $this->input_css ) / 1000 );
+		} else {
+			return ( strlen( $this->output_css_plain ) / 1000 );
+		}
+	}
+
+}
diff --git a/csstidy/class.csstidy.php b/csstidy/class.csstidy.php
new file mode 100644
index 0000000..76c5af5
--- /dev/null
+++ b/csstidy/class.csstidy.php
@@ -0,0 +1,1307 @@
+<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
+/**
+ * CSSTidy - CSS Parser and Optimiser
+ *
+ * CSS Parser class
+ *
+ * Copyright 2005, 2006, 2007 Florian Schmitz
+ *
+ * This file is part of CSSTidy.
+ *
+ *   CSSTidy is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published by
+ *   the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   CSSTidy is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * @license https://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007
+ * @author Brett Zamir (brettz9 at yahoo dot com) 2007
+ * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
+ * @author Cedric Morin (cedric at yterium dot com) 2010
+ */
+
+// phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged
+
+/**
+ * Defines ctype functions if required
+ *
+ * @version 1.0
+ */
+require_once __DIR__ . '/class.csstidy-ctype.php';
+
+/**
+ * Various CSS data needed for correct optimisations etc.
+ *
+ * @version 1.3
+ */
+require __DIR__ . '/data.inc.php';
+
+/**
+ * Contains a class for printing CSS code
+ *
+ * @version 1.0
+ */
+require __DIR__ . '/class.csstidy-print.php';
+
+/**
+ * Contains a class for optimising CSS code
+ *
+ * @version 1.0
+ */
+require __DIR__ . '/class.csstidy-optimise.php';
+
+/**
+ * CSS Parser class
+
+ * This class represents a CSS parser which reads CSS code and saves it in an array.
+ * In opposite to most other CSS parsers, it does not use regular expressions and
+ * thus has full CSS2 support and a higher reliability.
+ * Additional to that it applies some optimisations and fixes to the CSS code.
+ * An online version should be available here: https://cdburnerxp.se/cssparse/css_optimiser.php
+ *
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2006
+ * @version 1.3.1
+ */
+class csstidy { // phpcs:ignore
+
+	/**
+	 * Saves the parsed CSS. This array is empty if preserve_css is on.
+	 *
+	 * @var array
+	 * @access public
+	 */
+	public $css = array();
+	/**
+	 * Saves the parsed CSS (raw)
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $tokens = array();
+	/**
+	 * Printer class
+	 *
+	 * @see csstidy_print
+	 * @var object
+	 * @access public
+	 */
+	public $print;
+	/**
+	 * Optimiser class
+	 *
+	 * @see csstidy_optimise
+	 * @var object
+	 * @access private
+	 */
+	public $optimise;
+	/**
+	 * Saves the CSS charset (@charset)
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $charset = '';
+	/**
+	 * Saves all @import URLs
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $import = array();
+	/**
+	 * Saves the namespace
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $namespace = '';
+	/**
+	 * Contains the version of csstidy
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $version = '1.3';
+	/**
+	 * Stores the settings
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $settings = array();
+	/**
+	 * Saves the parser-status.
+	 *
+	 * Possible values:
+	 * - is = in selector
+	 * - ip = in property
+	 * - iv = in value
+	 * - instr = in string (started at " or ' or ( )
+	 * - ic = in comment (ignore everything)
+	 * - at = in @-block
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $status = 'is';
+	/**
+	 * Saves the current at rule (@media)
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $at = '';
+	/**
+	 * Saves the current selector
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $selector = '';
+	/**
+	 * Saves the current property
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $property = '';
+	/**
+	 * Saves the position of , in selectors
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $sel_separate = array();
+	/**
+	 * Saves the current value
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $value = '';
+	/**
+	 * Saves the current sub-value
+	 *
+	 * Example for a subvalue:
+	 * background:url(foo.png) red no-repeat;
+	 * "url(foo.png)", "red", and  "no-repeat" are subvalues,
+	 * separated by whitespace
+	 *
+	 * @var string
+	 * @access private
+	 */
+	public $sub_value = '';
+	/**
+	 * Array which saves all subvalues for a property.
+	 *
+	 * @var array
+	 * @see sub_value
+	 * @access private
+	 */
+	public $sub_value_arr = array();
+	/**
+	 * Saves the stack of characters that opened the current strings
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $str_char = array();
+	/**
+	 * Current strings.
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $cur_string = array();
+	/**
+	 * Status from which the parser switched to ic or instr
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $from = array();
+	/**
+	/**
+	 * =true if in invalid at-rule
+	 *
+	 * @var bool
+	 * @access private
+	 */
+	public $invalid_at = false;
+	/**
+	 * =true if something has been added to the current selector
+	 *
+	 * @var bool
+	 * @access private
+	 */
+	public $added = false;
+	/**
+	 * Array which saves the message log
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $log = array();
+	/**
+	 * Saves the line number
+	 *
+	 * @var integer
+	 * @access private
+	 */
+	public $line = 1;
+	/**
+	 * Marks if we need to leave quotes for a string
+	 *
+	 * @var array
+	 * @access private
+	 */
+	public $quoted_string = array();
+
+	/**
+	 * List of tokens
+	 *
+	 * @var string
+	 */
+	public $tokens_list = '';
+
+	/**
+	 * Loads standard template and sets default settings.
+	 *
+	 * @access private
+	 * @version 1.3
+	 */
+	public function __construct() {
+		$this->settings['remove_bslash']        = true;
+		$this->settings['compress_colors']      = true;
+		$this->settings['compress_font-weight'] = true;
+		$this->settings['lowercase_s']          = false;
+
+		/*
+		1 common shorthands optimization
+		2 + font property optimization
+		3 + background property optimization
+		 */
+		$this->settings['optimise_shorthands'] = 1;
+		$this->settings['remove_last_;']       = true;
+		/* rewrite all properties with low case, better for later gzip OK, safe*/
+		$this->settings['case_properties'] = 1;
+
+		/*
+		 * sort properties in alpabetic order, better for later gzip
+		 * but can cause trouble in case of overiding same propertie or using hack
+		 */
+		$this->settings['sort_properties'] = false;
+
+		/*
+		1, 3, 5, etc -- enable sorting selectors inside @media: a{}b{}c{}
+		2, 5, 8, etc -- enable sorting selectors inside one CSS declaration: a,b,c{}
+		preserve order by default cause it can break functionnality
+		 */
+		$this->settings['sort_selectors'] = 0;
+		/* is dangeroues to be used: CSS is broken sometimes */
+		$this->settings['merge_selectors'] = 0;
+		/* preserve or not browser hacks */
+		$this->settings['discard_invalid_selectors']  = false;
+		$this->settings['discard_invalid_properties'] = false;
+		$this->settings['css_level']                  = 'CSS2.1';
+		$this->settings['preserve_css']               = false;
+		$this->settings['timestamp']                  = false;
+		$this->settings['template']                   = ''; // say that propertie exist.
+		$this->set_cfg( 'template', 'default' ); // call load_template.
+		/* Tells csstidy_optimise to keep leading zeros on decimal numbers, e.g., 0.7 */
+		$this->settings['preserve_leading_zeros'] = false;
+		$this->optimise                           = new csstidy_optimise( $this );
+
+		$this->tokens_list = & $GLOBALS['csstidy']['tokens'];
+	}
+
+	/**
+	 * Call the construct function.
+	 */
+	public function csstidy() {
+		$this->__construct();
+	}
+
+	/**
+	 * Get the value of a setting.
+	 *
+	 * @param string $setting - the settings.
+	 * @access public
+	 * @return mixed
+	 * @version 1.0
+	 */
+	public function get_cfg( $setting ) {
+		if ( isset( $this->settings[ $setting ] ) ) {
+			return $this->settings[ $setting ];
+		}
+		return false;
+	}
+
+	/**
+	 * Load a template
+	 *
+	 * @param string $template used by set_cfg to load a template via a configuration setting.
+	 * @access private
+	 * @version 1.4
+	 */
+	public function _load_template( $template ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
+		switch ( $template ) {
+			case 'default':
+				$this->load_template( 'default' );
+				break;
+
+			case 'highest':
+				$this->load_template( 'highest_compression' );
+				break;
+
+			case 'high':
+				$this->load_template( 'high_compression' );
+				break;
+
+			case 'low':
+				$this->load_template( 'low_compression' );
+				break;
+
+			default:
+				$this->load_template( $template );
+				break;
+		}
+	}
+
+	/**
+	 * Set the value of a setting.
+	 *
+	 * @param string $setting - the setting.
+	 * @param mixed  $value - the value we're setting.
+	 * @access public
+	 * @return bool
+	 * @version 1.0
+	 */
+	public function set_cfg( $setting, $value = null ) {
+		if ( is_array( $setting ) && null === $value ) {
+			foreach ( $setting as $setprop => $setval ) {
+				$this->settings[ $setprop ] = $setval;
+			}
+			if ( array_key_exists( 'template', $setting ) ) {
+				$this->_load_template( $this->settings['template'] );
+			}
+			return true;
+		} elseif ( isset( $this->settings[ $setting ] ) && '' !== $value ) {
+			$this->settings[ $setting ] = $value;
+			if ( 'template' === $setting ) {
+				$this->_load_template( $this->settings['template'] );
+			}
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Adds a token to $this->tokens
+	 *
+	 * @param mixed  $type - the type.
+	 * @param string $data - data.
+	 * @param bool   $do add a token even if preserve_css is off.
+	 * @access private
+	 * @version 1.0
+	 */
+	public function _add_token( $type, $data, $do = false ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
+		if ( $this->get_cfg( 'preserve_css' ) || $do ) {
+			$this->tokens[] = array( $type, ( COMMENT === $type ) ? $data : trim( $data ) );
+		}
+	}
+
+	/**
+	 * Add a message to the message log
+	 *
+	 * @param string  $message - the message.
+	 * @param string  $type - the type of message.
+	 * @param integer $line - the line.
+	 * @access private
+	 * @version 1.0
+	 */
+	public function log( $message, $type, $line = -1 ) {
+		if ( -1 === $line ) {
+			$line = $this->line;
+		}
+		$line = (int) $line;
+		$add  = array(
+			'm' => $message,
+			't' => $type,
+		);
+		if ( ! isset( $this->log[ $line ] ) || ! in_array( $add, $this->log[ $line ], true ) ) {
+			$this->log[ $line ][] = $add;
+		}
+	}
+
+	/**
+	 * Parse unicode notations and find a replacement character
+	 *
+	 * @param string  $string - a string.
+	 * @param integer $i - counting integer.
+	 * @access private
+	 * @return string
+	 * @version 1.2
+	 */
+	public function _unicode( &$string, &$i ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
+		++$i;
+		$add      = '';
+		$replaced = false;
+
+		while ( $i < strlen( $string ) && ( ctype_xdigit( $string[ $i ] ) || ctype_space( $string[ $i ] ) ) && strlen( $add ) < 6 ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
+			$add .= $string[ $i ];
+
+			if ( ctype_space( $string[ $i ] ) ) {
+				break;
+			}
+			$i++;
+		}
+
+		if ( hexdec( $add ) > 47 && hexdec( $add ) < 58 || hexdec( $add ) > 64 && hexdec( $add ) < 91 || hexdec( $add ) > 96 && hexdec( $add ) < 123 ) {
+			$this->log( 'Replaced unicode notation: Changed \\' . $add . ' to ' . chr( hexdec( $add ) ), 'Information' );
+			$add      = chr( hexdec( $add ) );
+			$replaced = true;
+		} else {
+			$add = trim( '\\' . $add );
+		}
+
+		if ( @ctype_xdigit( $string[ $i + 1 ] ) && ctype_space( $string[ $i ] )
+						&& ! $replaced || ! ctype_space( $string[ $i ] ) ) {
+			$i--;
+		}
+
+		if ( '\\' !== $add || ! $this->get_cfg( 'remove_bslash' ) || strpos( $this->tokens_list, $string[ $i + 1 ] ) !== false ) {
+			return $add;
+		}
+
+		if ( '\\' === $add ) {
+			$this->log( 'Removed unnecessary backslash', 'Information' );
+		}
+		return '';
+	}
+
+	/**
+	 * Write formatted output to a file
+	 *
+	 * @param string $filename - the file na,e.
+	 * @param string $doctype when printing formatted, is a shorthand for the document type.
+	 * @param bool   $externalcss when printing formatted, indicates whether styles to be attached internally or as an external stylesheet.
+	 * @param string $title when printing formatted, is the title to be added in the head of the document.
+	 * @param string $lang when printing formatted, gives a two-letter language code to be added to the output.
+	 * @access public
+	 * @version 1.4
+	 */
+	public function write_page( $filename, $doctype = 'xhtml1.1', $externalcss = true, $title = '', $lang = 'en' ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+		$this->write( $filename, true );
+	}
+
+	/**
+	 * Write plain output to a file
+	 *
+	 * @param string $filename the file name.
+	 * @param bool   $formatted whether to print formatted or not.
+	 * @param string $doctype when printing formatted, is a shorthand for the document type.
+	 * @param bool   $externalcss when printing formatted, indicates whether styles to be attached internally or as an external stylesheet.
+	 * @param string $title when printing formatted, is the title to be added in the head of the document.
+	 * @param string $lang when printing formatted, gives a two-letter language code to be added to the output.
+	 * @param bool   $pre_code whether to add pre and code tags around the code (for light HTML formatted templates).
+	 * @access public
+	 * @version 1.4
+	 */
+	public function write( $filename, $formatted = false, $doctype = 'xhtml1.1', $externalcss = true, $title = '', $lang = 'en', $pre_code = true ) {
+		$filename .= ( $formatted ) ? '.xhtml' : '.css';
+
+		if ( ! is_dir( 'temp' ) ) {
+			$madedir = mkdir( 'temp' );
+			if ( ! $madedir ) {
+				print 'Could not make directory "temp" in ' . __DIR__;
+				exit;
+			}
+		}
+		$handle = fopen( 'temp/' . $filename, 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
+		if ( $handle ) {
+			if ( ! $formatted ) {
+				fwrite( $handle, $this->print->plain() ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
+			} else {
+				fwrite( $handle, $this->print->formatted_page( $doctype, $externalcss, $title, $lang, $pre_code ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
+			}
+		}
+		fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
+	}
+
+	/**
+	 * Loads a new template
+	 *
+	 * @param string $content either filename (if $from_file == true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default".
+	 * @param bool   $from_file uses $content as filename if true.
+	 * @access public
+	 * @version 1.1
+	 * @see http://csstidy.sourceforge.net/templates.php
+	 */
+	public function load_template( $content, $from_file = true ) {
+		$predefined_templates = & $GLOBALS['csstidy']['predefined_templates'];
+		if ( 'high_compression' === $content || 'default' === $content || 'highest_compression' === $content || 'low_compression' === $content ) {
+			$this->template = $predefined_templates[ $content ];
+			return;
+		}
+
+		if ( $from_file ) {
+			$content = strip_tags( file_get_contents( $content ), '<span>' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
+		}
+		$content  = str_replace( "\r\n", "\n", $content ); // Unify newlines (because the output also only uses \n).
+		$template = explode( '|', $content );
+
+		$this->template = array_replace( $this->template, $template );
+	}
+
+	/**
+	 * Starts parsing from URL
+	 *
+	 * @param string $url - the URL.
+	 * @access public
+	 * @version 1.0
+	 */
+	public function parse_from_url( $url ) {
+		return $this->parse( @file_get_contents( $url ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
+	}
+
+	/**
+	 * Checks if there is a token at the current position
+	 *
+	 * @param string  $string - the string we're checking.
+	 * @param integer $i - an int.
+	 * @access public
+	 * @version 1.11
+	 */
+	public function is_token( &$string, $i ) {
+		return ( strpos( $this->tokens_list, $string[ $i ] ) !== false && ! self::escaped( $string, $i ) );
+	}
+
+	/**
+	 * Parses CSS in $string. The code is saved as array in $this->css
+	 *
+	 * @param string $string the CSS code.
+	 * @access public
+	 * @return bool
+	 * @version 1.1
+	 */
+	public function parse( $string ) {
+		// Temporarily set locale to en_US in order to handle floats properly.
+		$old = @setlocale( LC_ALL, 0 );
+		@setlocale( LC_ALL, 'C' );
+
+		// PHP bug? Settings need to be refreshed in PHP4.
+		$this->print = new csstidy_print( $this );
+
+		$at_rules                 = & $GLOBALS['csstidy']['at_rules'];
+		$quoted_string_properties = & $GLOBALS['csstidy']['quoted_string_properties'];
+
+		$this->css              = array();
+		$this->print->input_css = $string;
+		$string                 = str_replace( "\r\n", "\n", $string ) . ' ';
+		$cur_comment            = '';
+
+		for ( $i = 0, $size = strlen( $string ); $i < $size; $i++ ) {
+			if ( "\n" === $string[ $i ] || "\r" === $string[ $i ] ) {
+				++$this->line;
+			}
+
+			switch ( $this->status ) {
+				/* Case in at-block */
+				case 'at':
+					if ( self::is_token( $string, $i ) ) {
+						if ( '/' === $string[ $i ] && '*' === @$string[ $i + 1 ] ) {
+							$this->status = 'ic';
+							++$i;
+							$this->from[] = 'at';
+						} elseif ( '{' === $string[ $i ] ) {
+							$this->status = 'is';
+							$this->at     = $this->css_new_media_section( $this->at );
+							$this->_add_token( AT_START, $this->at );
+						} elseif ( ',' === $string[ $i ] ) {
+							$this->at = trim( $this->at ) . ',';
+						} elseif ( '\\' === $string[ $i ] ) {
+							$this->at .= $this->_unicode( $string, $i );
+						} elseif ( in_array( $string[ $i ], array( '(', ')', ':', '.', '/' ), true ) ) {
+							// fix for complicated media, i.e @media screen and (-webkit-min-device-pixel-ratio:1.5)
+							// '/' is included for ratios in Opera: (-o-min-device-pixel-ratio: 3/2).
+							$this->at .= $string[ $i ];
+						}
+					} else {
+						$lastpos = strlen( $this->at ) - 1;
+						if ( ! ( ( ctype_space( $this->at[ $lastpos ] ) || self::is_token( $this->at, $lastpos ) && ',' === $this->at[ $lastpos ] ) && ctype_space( $string[ $i ] ) ) ) {
+							$this->at .= $string[ $i ];
+						}
+					}
+					break;
+
+				/* Case in-selector */
+				case 'is':
+					if ( self::is_token( $string, $i ) ) {
+						if ( '/' === $string[ $i ] && '*' === @$string[ $i + 1 ] && '' === trim( $this->selector ) ) {
+							$this->status = 'ic';
+							++$i;
+							$this->from[] = 'is';
+						} elseif ( '@' === $string[ $i ] && '' === trim( $this->selector ) ) {
+							// Check for at-rule.
+							$this->invalid_at = true;
+							foreach ( $at_rules as $name => $type ) {
+								if ( ! strcasecmp( substr( $string, $i + 1, strlen( $name ) ), $name ) ) {
+									( 'at' === $type ) ? $this->at = '@' . $name : $this->selector = '@' . $name;
+									$this->status                  = $type;
+									$i                            += strlen( $name );
+									$this->invalid_at              = false;
+								}
+							}
+
+							if ( $this->invalid_at ) {
+								$this->selector  = '@';
+								$invalid_at_name = '';
+								for ( $j = $i + 1; $j < $size; ++$j ) {
+									if ( ! ctype_alpha( $string[ $j ] ) ) {
+										break;
+									}
+									$invalid_at_name .= $string[ $j ];
+								}
+								$this->log( 'Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning' );
+							}
+						} elseif ( ( '"' === $string[ $i ] || "'" === $string[ $i ] ) ) {
+							$this->cur_string[] = $string[ $i ];
+							$this->status       = 'instr';
+							$this->str_char[]   = $string[ $i ];
+							$this->from[]       = 'is';
+							/* fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */
+							$this->quoted_string[] = ( '=' === $string[ $i - 1 ] );
+						} elseif ( $this->invalid_at && ';' === $string[ $i ] ) {
+							$this->invalid_at = false;
+							$this->status     = 'is';
+						} elseif ( '{' === $string[ $i ] ) {
+							$this->status = 'ip';
+							if ( '' === $this->at ) {
+								$this->at = $this->css_new_media_section( DEFAULT_AT );
+							}
+							$this->selector = $this->css_new_selector( $this->at, $this->selector );
+							$this->_add_token( SEL_START, $this->selector );
+							$this->added = false;
+						} elseif ( '}' === $string[ $i ] ) {
+							$this->_add_token( AT_END, $this->at );
+							$this->at           = '';
+							$this->selector     = '';
+							$this->sel_separate = array();
+						} elseif ( ',' === $string[ $i ] ) {
+							$this->selector       = trim( $this->selector ) . ',';
+							$this->sel_separate[] = strlen( $this->selector );
+						} elseif ( '\\' === $string[ $i ] ) {
+							$this->selector .= $this->_unicode( $string, $i );
+						} elseif ( '*' === $string[ $i ] && @in_array( $string[ $i + 1 ], array( '.', '#', '[', ':' ), true ) ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedElseif
+							// remove unnecessary universal selector, FS#147.
+						} else {
+							$this->selector .= $string[ $i ];
+						}
+					} else {
+						$lastpos = strlen( $this->selector ) - 1;
+						if ( -1 === $lastpos || ! ( ( ctype_space( $this->selector[ $lastpos ] ) || self::is_token( $this->selector, $lastpos ) && ',' === $this->selector[ $lastpos ] ) && ctype_space( $string[ $i ] ) ) ) {
+							$this->selector .= $string[ $i ];
+						} elseif ( ctype_space( $string[ $i ] ) && $this->get_cfg( 'preserve_css' ) && ! $this->get_cfg( 'merge_selectors' ) ) {
+							$this->selector .= $string[ $i ];
+						}
+					}
+					break;
+
+				/* Case in-property */
+				case 'ip':
+					if ( self::is_token( $string, $i ) ) {
+						if ( ( ':' === $string[ $i ] || '=' === $string[ $i ] ) && '' !== $this->property ) {
+							$this->status = 'iv';
+							if ( ! $this->get_cfg( 'discard_invalid_properties' ) || self::property_is_valid( $this->property ) ) {
+								$this->property = $this->css_new_property( $this->at, $this->selector, $this->property );
+								$this->_add_token( PROPERTY, $this->property );
+							}
+						} elseif ( '/' === $string[ $i ] && '*' === @$string[ $i + 1 ] && '' === $this->property ) {
+							$this->status = 'ic';
+							++$i;
+							$this->from[] = 'ip';
+						} elseif ( '}' === $string[ $i ] ) {
+							$this->explode_selectors();
+							$this->status     = 'is';
+							$this->invalid_at = false;
+							$this->_add_token( SEL_END, $this->selector );
+							$this->selector = '';
+							$this->property = '';
+						} elseif ( ';' === $string[ $i ] ) {
+							$this->property = '';
+						} elseif ( '\\' === $string[ $i ] ) {
+							$this->property .= $this->_unicode( $string, $i );
+						} elseif ( '' === $this->property && ! ctype_space( $string[ $i ] ) ) {
+							// else this is dumb IE a hack, keep it.
+							$this->property .= $string[ $i ];
+						}
+					} elseif ( ! ctype_space( $string[ $i ] ) ) {
+						$this->property .= $string[ $i ];
+					}
+					break;
+
+				/* Case in-value */
+				case 'iv':
+					$pn = ( ( "\n" === $string[ $i ] || "\r" === $string[ $i ] ) && $this->property_is_next( $string, $i + 1 ) || strlen( $string ) - 1 === $i );
+					if ( ( self::is_token( $string, $i ) || $pn ) && ( ! ( ',' === $string[ $i ] && ! ctype_space( $string[ $i + 1 ] ) ) ) ) {
+						if ( '/' === $string[ $i ] && '*' === @$string[ $i + 1 ] ) {
+							$this->status = 'ic';
+							++$i;
+							$this->from[] = 'iv';
+						} elseif ( ( '"' === $string[ $i ] || "'" === $string[ $i ] || '(' === $string[ $i ] ) ) {
+							$this->cur_string[]    = $string[ $i ];
+							$this->str_char[]      = ( '(' === $string[ $i ] ) ? ')' : $string[ $i ];
+							$this->status          = 'instr';
+							$this->from[]          = 'iv';
+							$this->quoted_string[] = in_array( strtolower( $this->property ), $quoted_string_properties, true );
+						} elseif ( ',' === $string[ $i ] ) {
+							$this->sub_value = trim( $this->sub_value ) . ',';
+						} elseif ( '\\' === $string[ $i ] ) {
+							$this->sub_value .= $this->_unicode( $string, $i );
+						} elseif ( ';' === $string[ $i ] || $pn ) {
+							if ( '@' === $this->selector[0] && isset( $at_rules[ substr( $this->selector, 1 ) ] ) && 'iv' === $at_rules[ substr( $this->selector, 1 ) ] ) {
+								$this->status = 'is';
+
+								switch ( $this->selector ) {
+									case '@charset':
+										/* Add quotes to charset */
+										$this->sub_value_arr[] = '"' . trim( $this->sub_value ) . '"';
+										$this->charset         = $this->sub_value_arr[0];
+										break;
+									case '@namespace':
+										/* Add quotes to namespace */
+										$this->sub_value_arr[] = '"' . trim( $this->sub_value ) . '"';
+										$this->namespace       = implode( ' ', $this->sub_value_arr );
+										break;
+									case '@import':
+										$this->sub_value = trim( $this->sub_value );
+
+										if ( empty( $this->sub_value_arr ) ) {
+											// Quote URLs in imports only if they're not already inside url() and not already quoted.
+											if ( substr( $this->sub_value, 0, 4 ) !== 'url(' ) {
+												if ( ! ( substr( $this->sub_value, -1 ) === $this->sub_value[0] && in_array( $this->sub_value[0], array( "'", '"' ), true ) ) ) {
+													$this->sub_value = '"' . $this->sub_value . '"';
+												}
+											}
+										}
+
+										$this->sub_value_arr[] = $this->sub_value;
+										$this->import[]        = implode( ' ', $this->sub_value_arr );
+										break;
+								}
+
+								$this->sub_value_arr = array();
+								$this->sub_value     = '';
+								$this->selector      = '';
+								$this->sel_separate  = array();
+							} else {
+								$this->status = 'ip';
+							}
+						} elseif ( '}' !== $string[ $i ] ) {
+							$this->sub_value .= $string[ $i ];
+						}
+						if ( ( '}' === $string[ $i ] || ';' === $string[ $i ] || $pn ) && ! empty( $this->selector ) ) {
+							if ( '' === $this->at ) {
+								$this->at = $this->css_new_media_section( DEFAULT_AT );
+							}
+
+							// case settings.
+							if ( $this->get_cfg( 'lowercase_s' ) ) {
+								$this->selector = strtolower( $this->selector );
+							}
+							$this->property = strtolower( $this->property );
+
+							$this->optimise->subvalue();
+							if ( '' !== $this->sub_value ) {
+								if ( substr( $this->sub_value, 0, 6 ) === 'format' ) {
+									$format_strings = self::parse_string_list( substr( $this->sub_value, 7, -1 ) );
+									if ( ! $format_strings ) {
+										$this->sub_value = '';
+									} else {
+										$this->sub_value = 'format(';
+
+										foreach ( $format_strings as $format_string ) {
+											$this->sub_value .= '"' . str_replace( '"', '\\"', $format_string ) . '",';
+										}
+
+										$this->sub_value = substr( $this->sub_value, 0, -1 ) . ')';
+									}
+								}
+								if ( '' !== $this->sub_value ) {
+									$this->sub_value_arr[] = $this->sub_value;
+								}
+								$this->sub_value = '';
+							}
+
+							$this->value = array_shift( $this->sub_value_arr );
+							while ( $this->sub_value_arr ) {
+								$this->value .= ' ' . array_shift( $this->sub_value_arr );
+							}
+
+							$this->optimise->value();
+
+							$valid = self::property_is_valid( $this->property );
+							if ( ( ! $this->invalid_at || $this->get_cfg( 'preserve_css' ) ) && ( ! $this->get_cfg( 'discard_invalid_properties' ) || $valid ) ) {
+								$this->css_add_property( $this->at, $this->selector, $this->property, $this->value );
+								$this->_add_token( VALUE, $this->value );
+								$this->optimise->shorthands();
+							}
+							if ( ! $valid ) {
+								if ( $this->get_cfg( 'discard_invalid_properties' ) ) {
+									$this->log( 'Removed invalid property: ' . $this->property, 'Warning' );
+								} else {
+									$this->log( 'Invalid property in ' . strtoupper( $this->get_cfg( 'css_level' ) ) . ': ' . $this->property, 'Warning' );
+								}
+							}
+
+							$this->property      = '';
+							$this->sub_value_arr = array();
+							$this->value         = '';
+						}
+						if ( '}' === $string[ $i ] ) {
+							$this->explode_selectors();
+							$this->_add_token( SEL_END, $this->selector );
+							$this->status     = 'is';
+							$this->invalid_at = false;
+							$this->selector   = '';
+						}
+					} elseif ( ! $pn ) {
+						$this->sub_value .= $string[ $i ];
+
+						if ( ctype_space( $string[ $i ] ) || ',' === $string[ $i ] ) {
+							$this->optimise->subvalue();
+							if ( '' !== $this->sub_value ) {
+								$this->sub_value_arr[] = $this->sub_value;
+								$this->sub_value       = '';
+							}
+						}
+					}
+					break;
+
+				/* Case in string */
+				case 'instr':
+					$_str_char   = $this->str_char[ count( $this->str_char ) - 1 ];
+					$_cur_string = $this->cur_string[ count( $this->cur_string ) - 1 ];
+					$temp_add    = $string[ $i ];
+
+					// Add another string to the stack. Strings can't be nested inside of quotes, only parentheses, but
+					// parentheticals can be nested more than once.
+					if ( ')' === $_str_char && ( '(' === $string[ $i ] || '"' === $string[ $i ] || '\'' === $string[ $i ] ) && ! self::escaped( $string, $i ) ) {
+						$this->cur_string[]    = $string[ $i ];
+						$this->str_char[]      = $string[ $i ] === '(' ? ')' : $string[ $i ];
+						$this->from[]          = 'instr';
+						$this->quoted_string[] = ! ( '(' === $string[ $i ] );
+						continue 2;
+					}
+
+					if ( ')' !== $_str_char && ( "\n" === $string[ $i ] || "\r" === $string[ $i ] ) && ! ( '\\' === $string[ $i - 1 ] && ! self::escaped( $string, $i - 1 ) ) ) {
+						$temp_add = '\\A';
+						$this->log( 'Fixed incorrect newline in string', 'Warning' );
+					}
+
+					$_cur_string .= $temp_add;
+
+					if ( $string[ $i ] === $_str_char && ! self::escaped( $string, $i ) ) {
+						$_quoted_string = array_pop( $this->quoted_string );
+
+						$this->status = array_pop( $this->from );
+
+						if ( ! preg_match( '|[' . implode( '', $GLOBALS['csstidy']['whitespace'] ) . ']|uis', $_cur_string ) && 'content' !== $this->property ) {
+							if ( ! $_quoted_string ) {
+								if ( ')' !== $_str_char ) {
+									// Convert properties like
+									// font-family: 'Arial';
+									// to
+									// font-family: Arial;
+									// or
+									// url("abc")
+									// to
+									// url(abc).
+									$_cur_string = substr( $_cur_string, 1, -1 );
+								}
+							} else {
+								$_quoted_string = false;
+							}
+						}
+
+						array_pop( $this->cur_string );
+						array_pop( $this->str_char );
+
+						if ( ')' === $_str_char ) {
+							$_cur_string = '(' . trim( substr( $_cur_string, 1, -1 ) ) . ')';
+						}
+
+						if ( 'iv' === $this->status ) {
+							if ( ! $_quoted_string ) {
+								if ( strpos( $_cur_string, ',' ) !== false ) {
+									// we can on only remove space next to ','.
+									$_cur_string = implode( ',', array_map( 'trim', explode( ',', $_cur_string ) ) );
+								}
+								// and multiple spaces (too expensive).
+								if ( strpos( $_cur_string, '  ' ) !== false ) {
+									$_cur_string = preg_replace( ',\s+,', ' ', $_cur_string );
+								}
+							}
+							$this->sub_value .= $_cur_string;
+						} elseif ( 'is' === $this->status ) {
+							$this->selector .= $_cur_string;
+						} elseif ( 'instr' === $this->status ) {
+							$this->cur_string[ count( $this->cur_string ) - 1 ] .= $_cur_string;
+						}
+					} else {
+						$this->cur_string[ count( $this->cur_string ) - 1 ] = $_cur_string;
+					}
+					break;
+
+				/* Case in-comment */
+				case 'ic':
+					if ( '*' === $string[ $i ] && '/' === $string[ $i + 1 ] ) {
+						$this->status = array_pop( $this->from );
+						$i++;
+						$this->_add_token( COMMENT, $cur_comment );
+						$cur_comment = '';
+					} else {
+						$cur_comment .= $string[ $i ];
+					}
+					break;
+			}
+		}
+
+		$this->optimise->postparse();
+
+		$this->print->_reset();
+
+		@setlocale( LC_ALL, $old ); // Set locale back to original setting.
+
+		return ! ( empty( $this->css ) && empty( $this->import ) && empty( $this->charset ) && empty( $this->tokens ) && empty( $this->namespace ) );
+	}
+
+	/**
+	 * Explodes selectors
+	 *
+	 * @access private
+	 * @version 1.0
+	 */
+	public function explode_selectors() {
+		// Explode multiple selectors.
+		if ( $this->get_cfg( 'merge_selectors' ) === 1 ) {
+			$new_sels             = array();
+			$lastpos              = 0;
+			$this->sel_separate[] = strlen( $this->selector );
+			foreach ( $this->sel_separate as $num => $pos ) {
+				if ( count( $this->sel_separate ) - 1 === $num ) {
+					++$pos;
+				}
+
+				$new_sels[] = substr( $this->selector, $lastpos, $pos - $lastpos - 1 );
+				$lastpos    = $pos;
+			}
+
+			if ( count( $new_sels ) > 1 ) {
+				foreach ( $new_sels as $selector ) {
+					if ( isset( $this->css[ $this->at ][ $this->selector ] ) ) {
+						$this->merge_css_blocks( $this->at, $selector, $this->css[ $this->at ][ $this->selector ] );
+					}
+				}
+				unset( $this->css[ $this->at ][ $this->selector ] );
+			}
+		}
+		$this->sel_separate = array();
+	}
+
+	/**
+	 * Checks if a character is escaped (and returns true if it is)
+	 *
+	 * @param string  $string - the string.
+	 * @param integer $pos - the position.
+	 * @access public
+	 * @return bool
+	 * @version 1.02
+	 */
+	public static function escaped( &$string, $pos ) {
+		return ! ( @( '\\' !== $string[ $pos - 1 ] ) || self::escaped( $string, $pos - 1 ) );
+	}
+
+	/**
+	 * Adds a property with value to the existing CSS code
+	 *
+	 * @param string $media - the media.
+	 * @param string $selector - the selector.
+	 * @param string $property - the property.
+	 * @param string $new_val - new value.
+	 * @access private
+	 * @version 1.2
+	 */
+	public function css_add_property( $media, $selector, $property, $new_val ) {
+		if ( $this->get_cfg( 'preserve_css' ) || '' === trim( $new_val ) ) {
+			return;
+		}
+
+		$this->added = true;
+		if ( isset( $this->css[ $media ][ $selector ][ $property ] ) ) {
+			if ( ( self::is_important( $this->css[ $media ][ $selector ][ $property ] ) && self::is_important( $new_val ) ) || ! self::is_important( $this->css[ $media ][ $selector ][ $property ] ) ) {
+				$this->css[ $media ][ $selector ][ $property ] = trim( $new_val );
+			}
+		} else {
+			$this->css[ $media ][ $selector ][ $property ] = trim( $new_val );
+		}
+	}
+
+	/**
+	 * Start a new media section.
+	 * Check if the media is not already known,
+	 * else rename it with extra spaces
+	 * to avoid merging
+	 *
+	 * @param string $media - the media.
+	 * @return string
+	 */
+	public function css_new_media_section( $media ) {
+		if ( $this->get_cfg( 'preserve_css' ) ) {
+			return $media;
+		}
+
+		// if the last @media is the same as this keep it.
+		if ( ! $this->css || ! is_array( $this->css ) || empty( $this->css ) ) {
+			return $media;
+		}
+		end( $this->css );
+		$at = current( $this->css );
+		if ( $at === $media ) {
+			return $media;
+		}
+		while ( isset( $this->css[ $media ] ) ) {
+			if ( is_numeric( $media ) ) {
+				$media++;
+			} else {
+				$media .= ' ';
+			}
+		}
+		return $media;
+	}
+
+	/**
+	 * Start a new selector.
+	 * If already referenced in this media section,
+	 * rename it with extra space to avoid merging
+	 * except if merging is required,
+	 * or last selector is the same (merge siblings)
+	 *
+	 * Never merge @font-face
+	 *
+	 * @param string $media - the media.
+	 * @param string $selector - the selector.
+	 * @return string
+	 */
+	public function css_new_selector( $media, $selector ) {
+		if ( $this->get_cfg( 'preserve_css' ) ) {
+			return $selector;
+		}
+		$selector = trim( $selector );
+		if ( strncmp( $selector, '@font-face', 10 ) !== 0 ) {
+			if ( $this->settings['merge_selectors'] ) {
+				return $selector;
+			}
+
+			if ( ! $this->css || ! isset( $this->css[ $media ] ) || ! $this->css[ $media ] ) {
+				return $selector;
+			}
+
+			// if last is the same, keep it.
+			end( $this->css[ $media ] );
+			$sel = current( $this->css[ $media ] );
+			if ( $sel === $selector ) {
+				return $selector;
+			}
+		}
+
+		while ( isset( $this->css[ $media ][ $selector ] ) ) {
+			$selector .= ' ';
+		}
+		return $selector;
+	}
+
+	/**
+	 * Start a new propertie.
+	 * If already references in this selector,
+	 * rename it with extra space to avoid override
+	 *
+	 * @param string $media - the media.
+	 * @param string $selector - the selector.
+	 * @param string $property - the property.
+	 * @return string
+	 */
+	public function css_new_property( $media, $selector, $property ) {
+		if ( $this->get_cfg( 'preserve_css' ) ) {
+			return $property;
+		}
+		if ( ! $this->css || ! isset( $this->css[ $media ][ $selector ] ) || ! $this->css[ $media ][ $selector ] ) {
+			return $property;
+		}
+
+		while ( isset( $this->css[ $media ][ $selector ][ $property ] ) ) {
+			$property .= ' ';
+		}
+
+		return $property;
+	}
+
+	/**
+	 * Adds CSS to an existing media/selector
+	 *
+	 * @param string $media - the media.
+	 * @param string $selector - the selector.
+	 * @param array  $css_add - css being added.
+	 * @access private
+	 * @version 1.1
+	 */
+	public function merge_css_blocks( $media, $selector, $css_add ) {
+		foreach ( $css_add as $property => $value ) {
+			$this->css_add_property( $media, $selector, $property, $value, false );
+		}
+	}
+
+	/**
+	 * Checks if $value is !important.
+	 *
+	 * @param string $value - the value.
+	 * @return bool
+	 * @access public
+	 * @version 1.0
+	 */
+	public static function is_important( &$value ) {
+		return ( ! strcasecmp( substr( str_replace( $GLOBALS['csstidy']['whitespace'], '', $value ), -10, 10 ), '!important' ) );
+	}
+
+	/**
+	 * Returns a value without !important
+	 *
+	 * @param string $value - the value.
+	 * @return string
+	 * @access public
+	 * @version 1.0
+	 */
+	public static function gvw_important( $value ) {
+		if ( self::is_important( $value ) ) {
+			$value = trim( $value );
+			$value = substr( $value, 0, -9 );
+			$value = trim( $value );
+			$value = substr( $value, 0, -1 );
+			$value = trim( $value );
+			return $value;
+		}
+		return $value;
+	}
+
+	/**
+	 * Checks if the next word in a string from pos is a CSS property
+	 *
+	 * @param string  $istring - if it's a string.
+	 * @param integer $pos - position.
+	 * @return bool
+	 * @access private
+	 * @version 1.2
+	 */
+	public function property_is_next( $istring, $pos ) {
+		$all_properties = & $GLOBALS['csstidy']['all_properties'];
+		$istring        = substr( $istring, $pos, strlen( $istring ) - $pos );
+		$pos            = strpos( $istring, ':' );
+		if ( false === $pos ) {
+			return false;
+		}
+		$istring = strtolower( trim( substr( $istring, 0, $pos ) ) );
+		if ( isset( $all_properties[ $istring ] ) ) {
+			$this->log( 'Added semicolon to the end of declaration', 'Warning' );
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Checks if a property is valid
+	 *
+	 * @param string $property - the property.
+	 * @return bool;
+	 * @access public
+	 * @version 1.0
+	 */
+	public function property_is_valid( $property ) {
+		$property = strtolower( $property );
+		if ( in_array( trim( $property ), $GLOBALS['csstidy']['multiple_properties'], true ) ) {
+			$property = trim( $property );
+		}
+		$all_properties = & $GLOBALS['csstidy']['all_properties'];
+		return ( isset( $all_properties[ $property ] ) && strpos( $all_properties[ $property ], strtoupper( $this->get_cfg( 'css_level' ) ) ) !== false );
+	}
+
+	/**
+	 * Accepts a list of strings (e.g., the argument to format() in a @font-face src property)
+	 * and returns a list of the strings.  Converts things like:
+	 *
+	 * Format(abc) => format("abc")
+	 * format(abc def) => format("abc","def")
+	 * format(abc "def") => format("abc","def")
+	 * format(abc, def, ghi) => format("abc","def","ghi")
+	 * format("abc",'def') => format("abc","def")
+	 * format("abc, def, ghi") => format("abc, def, ghi")
+	 *
+	 * @param string $value - the value.
+	 * @return array
+	 */
+	public function parse_string_list( $value ) {
+		$value = trim( $value );
+
+		// Case: if it's empty.
+		if ( ! $value ) {
+			return array();
+		}
+
+		$strings = array();
+
+		$in_str         = false;
+		$current_string = '';
+
+		for ( $i = 0, $_len = strlen( $value ); $i < $_len; $i++ ) {
+			if ( ( ',' === $value[ $i ] || ' ' === $value[ $i ] ) && true === $in_str ) {
+				$in_str         = false;
+				$strings[]      = $current_string;
+				$current_string = '';
+			} elseif ( '"' === $value[ $i ] || "'" === $value[ $i ] ) {
+				if ( $in_str === $value[ $i ] ) {
+					$strings[]      = $current_string;
+					$in_str         = false;
+					$current_string = '';
+					continue;
+				} elseif ( ! $in_str ) {
+					$in_str = $value[ $i ];
+				}
+			} else {
+				if ( $in_str ) {
+					$current_string .= $value[ $i ];
+				} else {
+					if ( ! preg_match( '/[\s,]/', $value[ $i ] ) ) {
+						$in_str         = true;
+						$current_string = $value[ $i ];
+					}
+				}
+			}
+		}
+
+		if ( $current_string ) {
+			$strings[] = $current_string;
+		}
+
+		return $strings;
+	}
+}
diff --git a/csstidy/cssparse-rtl.css b/csstidy/cssparse-rtl.css
new file mode 100644
index 0000000..f04a746
--- /dev/null
+++ b/csstidy/cssparse-rtl.css
@@ -0,0 +1,146 @@
+code#copytext {
+  white-space: pre;
+  font-family: Verdana;
+}
+
+.at {
+  color: darkblue;
+}
+
+.format {
+  color: gray;
+}
+
+.property {
+  color: green;
+}
+
+.selector {
+  color: blue;
+}
+
+.value {
+  color: red;
+  right: 500px;
+}
+
+.comment {
+  color: orange;
+}
+html, body {
+  font: 0.8em Verdana, Helvetica, sans-serif;
+  background: #F8F8F6;
+}
+
+code {
+  font-size: 1.2em;
+}
+
+div#rightcol {
+  padding-right: 32em;
+}
+
+fieldset {
+  display: block;
+  margin: 0.5em 0;
+  padding: 1em;
+  border: solid #7284AB 2px;
+}
+
+fieldset.code_output {
+  display: inline;
+}
+
+h1 {
+  font-size: 2em;
+}
+
+small {
+  font-size: 0.7em;
+}
+
+fieldset#field_input {
+  float: right;
+  margin: 0 0 1em 0.5em;
+}
+
+fieldset#options, fieldset#code_layout {
+  width: 31em;
+}
+
+input#submit {
+  clear: both;
+  display: block;
+  margin: 1em;
+}
+
+select {
+  margin: 2px 0 0;
+}
+
+label.block {
+  display: block;
+}
+
+legend {
+  background: #c4E1C3;
+  padding: 2px 4px;
+  border: dashed 1px;
+}
+
+textarea#css_text {
+  width: 27em;
+  height: 370px;
+  display: block;
+  margin-left: 1em;
+}
+
+.help {
+  cursor: help;
+}
+
+p.important {
+  border: solid 1px red;
+  font-weight: bold;
+  padding: 1em;
+  background: white;
+}
+
+p {
+  margin: 1em 0;
+}
+
+dl {
+  padding-right: 0.5em;
+}
+
+dt {
+  font-weight: bold;
+  margin: 0;
+  float: right;
+  clear: both;
+  height: 1.5em;
+}
+
+dd {
+  margin: 0 4em 0 0;
+  height: 1.5em;
+}
+
+fieldset#messages {
+  background: white;
+  padding: 0 1em 0 0;
+}
+
+fieldset#messages div {
+  height: 10em;
+  overflow: auto;
+}
+
+dd.Warning {
+  color: orange;
+}
+
+dd.Information {
+  color: green;
+}
diff --git a/csstidy/cssparse-rtl.min.css b/csstidy/cssparse-rtl.min.css
new file mode 100644
index 0000000..cea0e4f
--- /dev/null
+++ b/csstidy/cssparse-rtl.min.css
@@ -0,0 +1 @@
+code#copytext{font-family:Verdana;white-space:pre}.at{color:#00008b}.format{color:gray}.property{color:green}.selector{color:blue}.value{color:red;right:500px}.comment{color:orange}body,html{background:#f8f8f6;font:.8em Verdana,Helvetica,sans-serif}code{font-size:1.2em}div#rightcol{padding-right:32em}fieldset{border:2px solid #7284ab;display:block;margin:.5em 0;padding:1em}fieldset.code_output{display:inline}h1{font-size:2em}small{font-size:.7em}fieldset#field_input{float:right;margin:0 0 1em .5em}fieldset#code_layout,fieldset#options{width:31em}input#submit{clear:both;display:block;margin:1em}select{margin:2px 0 0}label.block{display:block}legend{background:#c4e1c3;border:1px dashed;padding:2px 4px}textarea#css_text{display:block;height:370px;margin-left:1em;width:27em}.help{cursor:help}p.important{background:#fff;border:1px solid red;font-weight:700;padding:1em}p{margin:1em 0}dl{padding-right:.5em}dt{clear:both;float:right;font-weight:700;margin:0}dd,dt{height:1.5em}dd{margin:0 4em 0 0}fieldset#messages{background:#fff;padding:0 1em 0 0}fieldset#messages div{height:10em;overflow:auto}dd.Warning{color:orange}dd.Information{color:green}
\ No newline at end of file
diff --git a/csstidy/cssparse.css b/csstidy/cssparse.css
new file mode 100644
index 0000000..4dab9b5
--- /dev/null
+++ b/csstidy/cssparse.css
@@ -0,0 +1,118 @@
+@import url("./cssparsed.css");
+
+html, body {
+font:0.8em Verdana,Helvetica,sans-serif;
+background:#F8F8F6;
+}
+
+code {
+font-size:1.2em;
+}
+
+div#rightcol {
+padding-left:32em;
+}
+
+fieldset {
+display:block;
+margin:0.5em 0;
+padding:1em;
+border:solid #7284AB 2px;
+}
+fieldset.code_output {
+display:inline;
+}
+
+h1 {
+font-size:2em;
+}
+
+small {
+font-size:0.7em;
+}
+
+fieldset#field_input {
+float:left;
+margin:0 0.5em 1em 0;
+}
+
+fieldset#options,fieldset#code_layout {
+width:31em;
+}
+
+input#submit {
+clear:both;
+display:block;
+margin:1em;
+}
+
+select {
+margin:2px 0 0;
+}
+
+label.block {
+display:block;
+}
+
+legend {
+background:#c4E1C3;
+padding:2px 4px;
+border:dashed 1px;
+}
+
+textarea#css_text {
+width:27em;
+height:370px;
+display:block;
+margin-right:1em;
+}
+
+.help {
+cursor:help;
+}
+
+p.important {
+border:solid 1px red;
+font-weight:bold;
+padding:1em;
+background:white;
+}
+
+p {
+margin:1em 0;
+}
+
+dl {
+padding-left:0.5em;
+}
+
+dt {
+font-weight:bold;
+margin:0;
+float:left;
+clear:both;
+height:1.5em;
+}
+
+dd {
+margin:0 0 0 4em;
+height:1.5em;
+}
+
+fieldset#messages {
+background:white;
+padding:0 0 0 1em;
+}
+
+fieldset#messages div {
+height:10em;
+overflow:auto;
+}
+
+dd.Warning {
+color:orange;
+}
+
+dd.Information {
+color:green;
+}
diff --git a/csstidy/cssparse.min.css b/csstidy/cssparse.min.css
new file mode 100644
index 0000000..74aaa8b
--- /dev/null
+++ b/csstidy/cssparse.min.css
@@ -0,0 +1 @@
+code#copytext{font-family:Verdana;white-space:pre}.at{color:#00008b}.format{color:gray}.property{color:green}.selector{color:blue}.value{color:red;left:500px}.comment{color:orange}body,html{background:#f8f8f6;font:.8em Verdana,Helvetica,sans-serif}code{font-size:1.2em}div#rightcol{padding-left:32em}fieldset{border:2px solid #7284ab;display:block;margin:.5em 0;padding:1em}fieldset.code_output{display:inline}h1{font-size:2em}small{font-size:.7em}fieldset#field_input{float:left;margin:0 .5em 1em 0}fieldset#code_layout,fieldset#options{width:31em}input#submit{clear:both;display:block;margin:1em}select{margin:2px 0 0}label.block{display:block}legend{background:#c4e1c3;border:1px dashed;padding:2px 4px}textarea#css_text{display:block;height:370px;margin-right:1em;width:27em}.help{cursor:help}p.important{background:#fff;border:1px solid red;font-weight:700;padding:1em}p{margin:1em 0}dl{padding-left:.5em}dt{clear:both;float:left;font-weight:700;margin:0}dd,dt{height:1.5em}dd{margin:0 0 0 4em}fieldset#messages{background:#fff;padding:0 0 0 1em}fieldset#messages div{height:10em;overflow:auto}dd.Warning{color:orange}dd.Information{color:green}
\ No newline at end of file
diff --git a/csstidy/cssparsed-rtl.css b/csstidy/cssparsed-rtl.css
new file mode 100644
index 0000000..5b8877a
--- /dev/null
+++ b/csstidy/cssparsed-rtl.css
@@ -0,0 +1,29 @@
+code#copytext {
+  white-space: pre;
+  font-family: Verdana;
+}
+
+.at {
+  color: darkblue;
+}
+
+.format {
+  color: gray;
+}
+
+.property {
+  color: green;
+}
+
+.selector {
+  color: blue;
+}
+
+.value {
+  color: red;
+  right: 500px;
+}
+
+.comment {
+  color: orange;
+}
diff --git a/csstidy/cssparsed-rtl.min.css b/csstidy/cssparsed-rtl.min.css
new file mode 100644
index 0000000..99cd80f
--- /dev/null
+++ b/csstidy/cssparsed-rtl.min.css
@@ -0,0 +1 @@
+code#copytext{font-family:Verdana;white-space:pre}.at{color:#00008b}.format{color:gray}.property{color:green}.selector{color:blue}.value{color:red;right:500px}.comment{color:orange}
\ No newline at end of file
diff --git a/csstidy/cssparsed.css b/csstidy/cssparsed.css
new file mode 100644
index 0000000..5aaf2bb
--- /dev/null
+++ b/csstidy/cssparsed.css
@@ -0,0 +1,29 @@
+code#copytext {
+	white-space: pre;
+	font-family: Verdana;
+}
+
+.at {
+color:darkblue;
+}
+
+.format {
+color:gray;
+}
+
+.property {
+color:green;
+}
+
+.selector {
+color:blue;
+}
+
+.value {
+color:red;
+left: 500px;
+}
+
+.comment {
+color:orange;
+}
\ No newline at end of file
diff --git a/csstidy/cssparsed.min.css b/csstidy/cssparsed.min.css
new file mode 100644
index 0000000..76b0020
--- /dev/null
+++ b/csstidy/cssparsed.min.css
@@ -0,0 +1 @@
+code#copytext{font-family:Verdana;white-space:pre}.at{color:#00008b}.format{color:gray}.property{color:green}.selector{color:blue}.value{color:red;left:500px}.comment{color:orange}
\ No newline at end of file
diff --git a/csstidy/data-wp.inc.php b/csstidy/data-wp.inc.php
new file mode 100644
index 0000000..518a5bb
--- /dev/null
+++ b/csstidy/data-wp.inc.php
@@ -0,0 +1,102 @@
+<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
+
+unset( $GLOBALS['csstidy']['all_properties']['binding'] );
+
+$GLOBALS['csstidy']['all_properties']['text-size-adjust'] = 'CSS3.0';
+
+// Support browser prefixes for properties only in the latest CSS draft
+foreach ( $GLOBALS['csstidy']['all_properties'] as $property => $levels ) {
+	if ( strpos( $levels, ',' ) === false ) {
+		$GLOBALS['csstidy']['all_properties'][ '-moz-' . $property ]    = $levels;
+		$GLOBALS['csstidy']['all_properties'][ '-webkit-' . $property ] = $levels;
+		$GLOBALS['csstidy']['all_properties'][ '-ms-' . $property ]     = $levels;
+		$GLOBALS['csstidy']['all_properties'][ '-o-' . $property ]      = $levels;
+		$GLOBALS['csstidy']['all_properties'][ '-khtml-' . $property ]  = $levels;
+
+		if ( in_array( $property, $GLOBALS['csstidy']['unit_values'], true ) ) {
+			$GLOBALS['csstidy']['unit_values'][] = '-moz-' . $property;
+			$GLOBALS['csstidy']['unit_values'][] = '-webkit-' . $property;
+			$GLOBALS['csstidy']['unit_values'][] = '-ms-' . $property;
+			$GLOBALS['csstidy']['unit_values'][] = '-o-' . $property;
+			$GLOBALS['csstidy']['unit_values'][] = '-khtml-' . $property;
+		}
+
+		if ( in_array( $property, $GLOBALS['csstidy']['color_values'], true ) ) {
+			$GLOBALS['csstidy']['color_values'][] = '-moz-' . $property;
+			$GLOBALS['csstidy']['color_values'][] = '-webkit-' . $property;
+			$GLOBALS['csstidy']['color_values'][] = '-ms-' . $property;
+			$GLOBALS['csstidy']['color_values'][] = '-o-' . $property;
+			$GLOBALS['csstidy']['color_values'][] = '-khtml-' . $property;
+		}
+	}
+}
+
+// Add `display` to the list of properties that can be used multiple times in a single selector
+$GLOBALS['csstidy']['multiple_properties'][] = 'display';
+
+// Allow vendor prefixes for any property that is allowed to be used multiple times inside a single selector
+foreach ( $GLOBALS['csstidy']['multiple_properties'] as $property ) {
+	if ( '-' !== $property[0] ) {
+		$GLOBALS['csstidy']['multiple_properties'][] = '-o-' . $property;
+		$GLOBALS['csstidy']['multiple_properties'][] = '-ms-' . $property;
+		$GLOBALS['csstidy']['multiple_properties'][] = '-webkit-' . $property;
+		$GLOBALS['csstidy']['multiple_properties'][] = '-moz-' . $property;
+		$GLOBALS['csstidy']['multiple_properties'][] = '-khtml-' . $property;
+	}
+}
+
+/**
+ * CSS Animation
+ *
+ * @see https://developer.mozilla.org/en/CSS/CSS_animations
+ */
+$GLOBALS['csstidy']['at_rules']['-webkit-keyframes'] = 'at';
+$GLOBALS['csstidy']['at_rules']['-moz-keyframes']    = 'at';
+$GLOBALS['csstidy']['at_rules']['-ms-keyframes']     = 'at';
+$GLOBALS['csstidy']['at_rules']['-o-keyframes']      = 'at';
+
+/**
+ * Non-standard viewport rule.
+ */
+$GLOBALS['csstidy']['at_rules']['viewport']         = 'is';
+$GLOBALS['csstidy']['at_rules']['-webkit-viewport'] = 'is';
+$GLOBALS['csstidy']['at_rules']['-moz-viewport']    = 'is';
+$GLOBALS['csstidy']['at_rules']['-ms-viewport']     = 'is';
+
+/**
+ * Non-standard CSS properties.  They're not part of any spec, but we say
+ * they're in all of them so that we can support them.
+ */
+$GLOBALS['csstidy']['all_properties']['-webkit-filter']             = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-moz-filter']                = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-ms-filter']                 = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['filter']                     = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scrollbar-face-color']       = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-ms-interpolation-mode']     = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-rendering']             = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-webkit-transform-origin-x'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-webkit-transform-origin-y'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-webkit-transform-origin-z'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-webkit-font-smoothing']     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-moz-osx-font-smoothing']    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-font-smooth']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-o-object-fit']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['object-fit']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['-o-object-position']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['object-position']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-overflow']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['zoom']                       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pointer-events']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-feature-settings']      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-kerning']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-language-override']     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-synthesis']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variant-alternates']    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variant-caps']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variant-east-asian']    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variant-ligatures']     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variant-numeric']       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variant-position']      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variation-settings']    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-height-step']           = 'CSS3.0';
+
diff --git a/csstidy/data.inc.php b/csstidy/data.inc.php
new file mode 100644
index 0000000..f88579a
--- /dev/null
+++ b/csstidy/data.inc.php
@@ -0,0 +1,777 @@
+<?php
+/**
+ * Various CSS Data for CSSTidy
+ *
+ * This file is part of CSSTidy.
+ *
+ * CSSTidy is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * CSSTidy is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with CSSTidy; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @license https://opensource.org/licenses/gpl-license.php GNU Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005, Nikolay Matsievsky (speed at webo dot name) 2010
+ */
+
+define( 'AT_START', 1 );
+define( 'AT_END', 2 );
+define( 'SEL_START', 3 );
+define( 'SEL_END', 4 );
+define( 'PROPERTY', 5 );
+define( 'VALUE', 6 );
+define( 'COMMENT', 7 );
+define( 'DEFAULT_AT', 41 );
+
+/**
+ * All whitespace allowed in CSS
+ *
+ * @global array $GLOBALS['csstidy']['whitespace']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['whitespace'] = array( ' ', "\n", "\t", "\r", "\x0B" );
+
+/**
+ * All CSS tokens used by csstidy
+ *
+ * @global string $GLOBALS['csstidy']['tokens']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['tokens'] = '/@}{;:=\'"(,\\!$%&)*+.<>?[]^`|~';
+
+/**
+ * All CSS units (CSS 3 units included)
+ *
+ * @see compress_numbers()
+ * @global array $GLOBALS['csstidy']['units']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['units'] = array( 'in', 'cm', 'mm', 'pt', 'pc', 'px', 'rem', 'em', '%', 'ex', 'gd', 'vw', 'vh', 'vm', 'deg', 'grad', 'rad', 'ms', 's', 'khz', 'hz' );
+
+/**
+ * Available at-rules
+ *
+ * @global array $GLOBALS['csstidy']['at_rules']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['at_rules'] = array(
+	'page'      => 'is',
+	'font-face' => 'is',
+	'charset'   => 'iv',
+	'import'    => 'iv',
+	'namespace' => 'iv',
+	'media'     => 'at',
+	'keyframes' => 'at',
+	'supports'  => 'at',
+);
+
+/**
+ * Properties that need a value with unit
+ *
+ * @todo CSS3 properties
+ * @see compress_numbers();
+ * @global array $GLOBALS['csstidy']['unit_values']
+ * @version 1.2
+ */
+$GLOBALS['csstidy']['unit_values'] = array(
+	'background',
+	'background-position',
+	'background-size',
+	'border',
+	'border-top',
+	'border-right',
+	'border-bottom',
+	'border-left',
+	'border-width',
+	'border-top-width',
+	'border-right-width',
+	'border-left-width',
+	'border-bottom-width',
+	'bottom',
+	'border-spacing',
+	'column-gap',
+	'column-width',
+	'font-size',
+	'height',
+	'left',
+	'margin',
+	'margin-top',
+	'margin-right',
+	'margin-bottom',
+	'margin-left',
+	'max-height',
+	'max-width',
+	'min-height',
+	'min-width',
+	'outline',
+	'outline-width',
+	'padding',
+	'padding-top',
+	'padding-right',
+	'padding-bottom',
+	'padding-left',
+	'perspective',
+	'right',
+	'top',
+	'text-indent',
+	'letter-spacing',
+	'word-spacing',
+	'width',
+);
+
+/**
+ * Properties that allow <color> as value
+ *
+ * @todo CSS3 properties
+ * @see compress_numbers();
+ * @global array $GLOBALS['csstidy']['color_values']
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['color_values']   = array();
+$GLOBALS['csstidy']['color_values'][] = 'background-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-top-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-right-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-bottom-color';
+$GLOBALS['csstidy']['color_values'][] = 'border-left-color';
+$GLOBALS['csstidy']['color_values'][] = 'color';
+$GLOBALS['csstidy']['color_values'][] = 'outline-color';
+$GLOBALS['csstidy']['color_values'][] = 'column-rule-color';
+
+/**
+ * Default values for the background properties
+ *
+ * @todo Possibly property names will change during CSS3 development
+ * @global array $GLOBALS['csstidy']['background_prop_default']
+ * @see dissolve_short_bg()
+ * @see merge_bg()
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['background_prop_default']                          = array();
+$GLOBALS['csstidy']['background_prop_default']['background-image']      = 'none';
+$GLOBALS['csstidy']['background_prop_default']['background-size']       = 'auto';
+$GLOBALS['csstidy']['background_prop_default']['background-repeat']     = 'repeat';
+$GLOBALS['csstidy']['background_prop_default']['background-position']   = '0 0';
+$GLOBALS['csstidy']['background_prop_default']['background-attachment'] = 'scroll';
+$GLOBALS['csstidy']['background_prop_default']['background-clip']       = 'border';
+$GLOBALS['csstidy']['background_prop_default']['background-origin']     = 'padding';
+$GLOBALS['csstidy']['background_prop_default']['background-color']      = 'transparent';
+
+/**
+ * Default values for the font properties
+ *
+ * @global array $GLOBALS['csstidy']['font_prop_default']
+ * @see merge_fonts()
+ * @version 1.3
+ */
+$GLOBALS['csstidy']['font_prop_default']                 = array();
+$GLOBALS['csstidy']['font_prop_default']['font-style']   = 'normal';
+$GLOBALS['csstidy']['font_prop_default']['font-variant'] = 'normal';
+$GLOBALS['csstidy']['font_prop_default']['font-weight']  = 'normal';
+$GLOBALS['csstidy']['font_prop_default']['font-size']    = '';
+$GLOBALS['csstidy']['font_prop_default']['line-height']  = '';
+$GLOBALS['csstidy']['font_prop_default']['font-family']  = '';
+
+/**
+ * A list of non-W3C color names which get replaced by their hex-codes
+ *
+ * @global array $GLOBALS['csstidy']['replace_colors']
+ * @see cut_color()
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['replace_colors']                         = array();
+$GLOBALS['csstidy']['replace_colors']['aliceblue']            = '#f0f8ff';
+$GLOBALS['csstidy']['replace_colors']['antiquewhite']         = '#faebd7';
+$GLOBALS['csstidy']['replace_colors']['aquamarine']           = '#7fffd4';
+$GLOBALS['csstidy']['replace_colors']['azure']                = '#f0ffff';
+$GLOBALS['csstidy']['replace_colors']['beige']                = '#f5f5dc';
+$GLOBALS['csstidy']['replace_colors']['bisque']               = '#ffe4c4';
+$GLOBALS['csstidy']['replace_colors']['blanchedalmond']       = '#ffebcd';
+$GLOBALS['csstidy']['replace_colors']['blueviolet']           = '#8a2be2';
+$GLOBALS['csstidy']['replace_colors']['brown']                = '#a52a2a';
+$GLOBALS['csstidy']['replace_colors']['burlywood']            = '#deb887';
+$GLOBALS['csstidy']['replace_colors']['cadetblue']            = '#5f9ea0';
+$GLOBALS['csstidy']['replace_colors']['chartreuse']           = '#7fff00';
+$GLOBALS['csstidy']['replace_colors']['chocolate']            = '#d2691e';
+$GLOBALS['csstidy']['replace_colors']['coral']                = '#ff7f50';
+$GLOBALS['csstidy']['replace_colors']['cornflowerblue']       = '#6495ed';
+$GLOBALS['csstidy']['replace_colors']['cornsilk']             = '#fff8dc';
+$GLOBALS['csstidy']['replace_colors']['crimson']              = '#dc143c';
+$GLOBALS['csstidy']['replace_colors']['cyan']                 = '#00ffff';
+$GLOBALS['csstidy']['replace_colors']['darkblue']             = '#00008b';
+$GLOBALS['csstidy']['replace_colors']['darkcyan']             = '#008b8b';
+$GLOBALS['csstidy']['replace_colors']['darkgoldenrod']        = '#b8860b';
+$GLOBALS['csstidy']['replace_colors']['darkgray']             = '#a9a9a9';
+$GLOBALS['csstidy']['replace_colors']['darkgreen']            = '#006400';
+$GLOBALS['csstidy']['replace_colors']['darkkhaki']            = '#bdb76b';
+$GLOBALS['csstidy']['replace_colors']['darkmagenta']          = '#8b008b';
+$GLOBALS['csstidy']['replace_colors']['darkolivegreen']       = '#556b2f';
+$GLOBALS['csstidy']['replace_colors']['darkorange']           = '#ff8c00';
+$GLOBALS['csstidy']['replace_colors']['darkorchid']           = '#9932cc';
+$GLOBALS['csstidy']['replace_colors']['darkred']              = '#8b0000';
+$GLOBALS['csstidy']['replace_colors']['darksalmon']           = '#e9967a';
+$GLOBALS['csstidy']['replace_colors']['darkseagreen']         = '#8fbc8f';
+$GLOBALS['csstidy']['replace_colors']['darkslateblue']        = '#483d8b';
+$GLOBALS['csstidy']['replace_colors']['darkslategray']        = '#2f4f4f';
+$GLOBALS['csstidy']['replace_colors']['darkturquoise']        = '#00ced1';
+$GLOBALS['csstidy']['replace_colors']['darkviolet']           = '#9400d3';
+$GLOBALS['csstidy']['replace_colors']['deeppink']             = '#ff1493';
+$GLOBALS['csstidy']['replace_colors']['deepskyblue']          = '#00bfff';
+$GLOBALS['csstidy']['replace_colors']['dimgray']              = '#696969';
+$GLOBALS['csstidy']['replace_colors']['dodgerblue']           = '#1e90ff';
+$GLOBALS['csstidy']['replace_colors']['feldspar']             = '#d19275';
+$GLOBALS['csstidy']['replace_colors']['firebrick']            = '#b22222';
+$GLOBALS['csstidy']['replace_colors']['floralwhite']          = '#fffaf0';
+$GLOBALS['csstidy']['replace_colors']['forestgreen']          = '#228b22';
+$GLOBALS['csstidy']['replace_colors']['gainsboro']            = '#dcdcdc';
+$GLOBALS['csstidy']['replace_colors']['ghostwhite']           = '#f8f8ff';
+$GLOBALS['csstidy']['replace_colors']['gold']                 = '#ffd700';
+$GLOBALS['csstidy']['replace_colors']['goldenrod']            = '#daa520';
+$GLOBALS['csstidy']['replace_colors']['greenyellow']          = '#adff2f';
+$GLOBALS['csstidy']['replace_colors']['honeydew']             = '#f0fff0';
+$GLOBALS['csstidy']['replace_colors']['hotpink']              = '#ff69b4';
+$GLOBALS['csstidy']['replace_colors']['indianred']            = '#cd5c5c';
+$GLOBALS['csstidy']['replace_colors']['indigo']               = '#4b0082';
+$GLOBALS['csstidy']['replace_colors']['ivory']                = '#fffff0';
+$GLOBALS['csstidy']['replace_colors']['khaki']                = '#f0e68c';
+$GLOBALS['csstidy']['replace_colors']['lavender']             = '#e6e6fa';
+$GLOBALS['csstidy']['replace_colors']['lavenderblush']        = '#fff0f5';
+$GLOBALS['csstidy']['replace_colors']['lawngreen']            = '#7cfc00';
+$GLOBALS['csstidy']['replace_colors']['lemonchiffon']         = '#fffacd';
+$GLOBALS['csstidy']['replace_colors']['lightblue']            = '#add8e6';
+$GLOBALS['csstidy']['replace_colors']['lightcoral']           = '#f08080';
+$GLOBALS['csstidy']['replace_colors']['lightcyan']            = '#e0ffff';
+$GLOBALS['csstidy']['replace_colors']['lightgoldenrodyellow'] = '#fafad2';
+$GLOBALS['csstidy']['replace_colors']['lightgrey']            = '#d3d3d3';
+$GLOBALS['csstidy']['replace_colors']['lightgreen']           = '#90ee90';
+$GLOBALS['csstidy']['replace_colors']['lightpink']            = '#ffb6c1';
+$GLOBALS['csstidy']['replace_colors']['lightsalmon']          = '#ffa07a';
+$GLOBALS['csstidy']['replace_colors']['lightseagreen']        = '#20b2aa';
+$GLOBALS['csstidy']['replace_colors']['lightskyblue']         = '#87cefa';
+$GLOBALS['csstidy']['replace_colors']['lightslateblue']       = '#8470ff';
+$GLOBALS['csstidy']['replace_colors']['lightslategray']       = '#778899';
+$GLOBALS['csstidy']['replace_colors']['lightsteelblue']       = '#b0c4de';
+$GLOBALS['csstidy']['replace_colors']['lightyellow']          = '#ffffe0';
+$GLOBALS['csstidy']['replace_colors']['limegreen']            = '#32cd32';
+$GLOBALS['csstidy']['replace_colors']['linen']                = '#faf0e6';
+$GLOBALS['csstidy']['replace_colors']['magenta']              = '#ff00ff';
+$GLOBALS['csstidy']['replace_colors']['mediumaquamarine']     = '#66cdaa';
+$GLOBALS['csstidy']['replace_colors']['mediumblue']           = '#0000cd';
+$GLOBALS['csstidy']['replace_colors']['mediumorchid']         = '#ba55d3';
+$GLOBALS['csstidy']['replace_colors']['mediumpurple']         = '#9370d8';
+$GLOBALS['csstidy']['replace_colors']['mediumseagreen']       = '#3cb371';
+$GLOBALS['csstidy']['replace_colors']['mediumslateblue']      = '#7b68ee';
+$GLOBALS['csstidy']['replace_colors']['mediumspringgreen']    = '#00fa9a';
+$GLOBALS['csstidy']['replace_colors']['mediumturquoise']      = '#48d1cc';
+$GLOBALS['csstidy']['replace_colors']['mediumvioletred']      = '#c71585';
+$GLOBALS['csstidy']['replace_colors']['midnightblue']         = '#191970';
+$GLOBALS['csstidy']['replace_colors']['mintcream']            = '#f5fffa';
+$GLOBALS['csstidy']['replace_colors']['mistyrose']            = '#ffe4e1';
+$GLOBALS['csstidy']['replace_colors']['moccasin']             = '#ffe4b5';
+$GLOBALS['csstidy']['replace_colors']['navajowhite']          = '#ffdead';
+$GLOBALS['csstidy']['replace_colors']['oldlace']              = '#fdf5e6';
+$GLOBALS['csstidy']['replace_colors']['olivedrab']            = '#6b8e23';
+$GLOBALS['csstidy']['replace_colors']['orangered']            = '#ff4500';
+$GLOBALS['csstidy']['replace_colors']['orchid']               = '#da70d6';
+$GLOBALS['csstidy']['replace_colors']['palegoldenrod']        = '#eee8aa';
+$GLOBALS['csstidy']['replace_colors']['palegreen']            = '#98fb98';
+$GLOBALS['csstidy']['replace_colors']['paleturquoise']        = '#afeeee';
+$GLOBALS['csstidy']['replace_colors']['palevioletred']        = '#d87093';
+$GLOBALS['csstidy']['replace_colors']['papayawhip']           = '#ffefd5';
+$GLOBALS['csstidy']['replace_colors']['peachpuff']            = '#ffdab9';
+$GLOBALS['csstidy']['replace_colors']['peru']                 = '#cd853f';
+$GLOBALS['csstidy']['replace_colors']['pink']                 = '#ffc0cb';
+$GLOBALS['csstidy']['replace_colors']['plum']                 = '#dda0dd';
+$GLOBALS['csstidy']['replace_colors']['powderblue']           = '#b0e0e6';
+$GLOBALS['csstidy']['replace_colors']['rosybrown']            = '#bc8f8f';
+$GLOBALS['csstidy']['replace_colors']['royalblue']            = '#4169e1';
+$GLOBALS['csstidy']['replace_colors']['saddlebrown']          = '#8b4513';
+$GLOBALS['csstidy']['replace_colors']['salmon']               = '#fa8072';
+$GLOBALS['csstidy']['replace_colors']['sandybrown']           = '#f4a460';
+$GLOBALS['csstidy']['replace_colors']['seagreen']             = '#2e8b57';
+$GLOBALS['csstidy']['replace_colors']['seashell']             = '#fff5ee';
+$GLOBALS['csstidy']['replace_colors']['sienna']               = '#a0522d';
+$GLOBALS['csstidy']['replace_colors']['skyblue']              = '#87ceeb';
+$GLOBALS['csstidy']['replace_colors']['slateblue']            = '#6a5acd';
+$GLOBALS['csstidy']['replace_colors']['slategray']            = '#708090';
+$GLOBALS['csstidy']['replace_colors']['snow']                 = '#fffafa';
+$GLOBALS['csstidy']['replace_colors']['springgreen']          = '#00ff7f';
+$GLOBALS['csstidy']['replace_colors']['steelblue']            = '#4682b4';
+$GLOBALS['csstidy']['replace_colors']['tan']                  = '#d2b48c';
+$GLOBALS['csstidy']['replace_colors']['thistle']              = '#d8bfd8';
+$GLOBALS['csstidy']['replace_colors']['tomato']               = '#ff6347';
+$GLOBALS['csstidy']['replace_colors']['turquoise']            = '#40e0d0';
+$GLOBALS['csstidy']['replace_colors']['violet']               = '#ee82ee';
+$GLOBALS['csstidy']['replace_colors']['violetred']            = '#d02090';
+$GLOBALS['csstidy']['replace_colors']['wheat']                = '#f5deb3';
+$GLOBALS['csstidy']['replace_colors']['whitesmoke']           = '#f5f5f5';
+$GLOBALS['csstidy']['replace_colors']['yellowgreen']          = '#9acd32';
+
+/**
+ * A list of all shorthand properties that are divided into four properties and/or have four subvalues
+ *
+ * @global array $GLOBALS['csstidy']['shorthands']
+ * @todo Are there new ones in CSS3?
+ * @see dissolve_4value_shorthands()
+ * @see merge_4value_shorthands()
+ * @version 1.0
+ */
+$GLOBALS['csstidy']['shorthands']                       = array();
+$GLOBALS['csstidy']['shorthands']['border-color']       = array( 'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color' );
+$GLOBALS['csstidy']['shorthands']['border-style']       = array( 'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style' );
+$GLOBALS['csstidy']['shorthands']['border-width']       = array( 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width' );
+$GLOBALS['csstidy']['shorthands']['margin']             = array( 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' );
+$GLOBALS['csstidy']['shorthands']['padding']            = array( 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' );
+$GLOBALS['csstidy']['shorthands']['-moz-border-radius'] = 0;
+
+/**
+ * All CSS Properties. Needed for csstidy::property_is_next()
+ *
+ * @global array $GLOBALS['csstidy']['all_properties']
+ * @todo Add CSS3 properties
+ * @version 1.0
+ * @see csstidy::property_is_next()
+ */
+$GLOBALS['csstidy']['all_properties']['align-content']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['align-items']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['align-self']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['alignment-adjust']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['alignment-baseline']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-delay']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-direction']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-duration']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-fill-mode']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-iteration-count']   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-name']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-play-state']        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['animation-timing-function']   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['appearance']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['azimuth']                     = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['backface-visibility']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background']                  = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-attachment']       = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-clip']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-color']            = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-image']            = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-origin']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-position']         = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-repeat']           = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['background-size']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['baseline-shift']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['binding']                     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bleed']                       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bookmark-label']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bookmark-level']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bookmark-state']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bookmark-target']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border']                      = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom']               = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-color']         = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-left-radius']   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-right-radius']  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-style']         = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-bottom-width']         = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-collapse']             = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-color']                = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-outset']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-repeat']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-slice']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-source']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-image-width']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-left']                 = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-left-color']           = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-left-style']           = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-left-width']           = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-radius']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-right']                = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-right-color']          = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-right-style']          = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-right-width']          = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-spacing']              = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-style']                = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top']                  = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-color']            = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-left-radius']      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-right-radius']     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-style']            = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-top-width']            = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['border-width']                = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['bottom']                      = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['box-decoration-break']        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['box-shadow']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['box-sizing']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['break-after']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['break-before']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['break-inside']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['caption-side']                = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['clear']                       = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['clip']                        = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['clip-path']                   = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['color']                       = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['color-profile']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-count']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-fill']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-gap']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-rule']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-rule-color']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-rule-style']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-rule-width']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-span']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['column-width']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['columns']                     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['content']                     = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['counter-increment']           = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['counter-reset']               = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['crop']                        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['cue']                         = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['cue-after']                   = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['cue-before']                  = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['cursor']                      = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['direction']                   = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['display']                     = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['dominant-baseline']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-after-adjust']   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-after-align']    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-before-adjust']  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-before-align']   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-size']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['drop-initial-value']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['elevation']                   = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['empty-cells']                 = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['fill']                        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['fit']                         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['fit-position']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex']                        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-align']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-basis']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-direction']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-flow']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-grow']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-line-pack']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-order']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-pack']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-shrink']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['flex-wrap']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['float']                       = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['float-offset']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font']                        = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-family']                 = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-size']                   = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-size-adjust']            = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-stretch']                = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-style']                  = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-variant']                = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['font-weight']                 = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid']                        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-area']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-auto-columns']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-auto-flow']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-auto-rows']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-column']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-columns']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-column-end']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-column-gap']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-column-start']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-gap']                    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-row']                    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-rows']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-row-end']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-row-gap']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-row-start']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-template']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-template-areas']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-template-columns']       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['grid-template-rows']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hanging-punctuation']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['height']                      = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-after']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-before']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-character']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-lines']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphenate-resource']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['hyphens']                     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['icon']                        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['image-orientation']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['image-rendering']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['image-resolution']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['inline-box-align']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['justify-content']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['justify-items']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['justify-self']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['left']                        = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['letter-spacing']              = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-break']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-height']                 = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-stacking']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-stacking-ruby']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-stacking-shift']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['line-stacking-strategy']      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['list-style']                  = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['list-style-image']            = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['list-style-position']         = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['list-style-type']             = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin']                      = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin-bottom']               = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin-left']                 = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin-right']                = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['margin-top']                  = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marker-offset']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marks']                       = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-direction']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-loop']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-play-count']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-speed']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['marquee-style']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['mask-clip']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['mask-composite']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['mask-image']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['mask-mode']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['mask-origin']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['mask-position']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['mask-repeat']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['mask-size']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['max-height']                  = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['max-width']                   = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['min-height']                  = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['min-width']                   = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['move-to']                     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-down']                    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-index']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-left']                    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-right']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['nav-up']                      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['object-fit']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['object-position']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['opacity']                     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['order']                       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['orphans']                     = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline']                     = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline-color']               = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline-offset']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline-style']               = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['outline-width']               = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow']                    = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow-style']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow-wrap']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow-x']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['overflow-y']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding']                     = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding-bottom']              = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding-left']                = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding-right']               = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['padding-top']                 = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page']                        = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page-break-after']            = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page-break-before']           = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page-break-inside']           = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['page-policy']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pause']                       = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pause-after']                 = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pause-before']                = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['perspective']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['perspective-origin']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['phonemes']                    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pitch']                       = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['pitch-range']                 = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['play-during']                 = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['position']                    = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['presentation-level']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['punctuation-trim']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['quotes']                      = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rendering-intent']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['resize']                      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rest']                        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rest-after']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rest-before']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['richness']                    = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['right']                       = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rotation']                    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['rotation-point']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['ruby-align']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['ruby-overhang']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['ruby-position']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['ruby-span']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-behavior']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-block']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-block-end']     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-block-start']   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-bottom']        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-inline']        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-inline-end']    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-inline-start']  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-left']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-right']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-margin-top']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-block']        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-block-end']    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-block-start']  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-bottom']       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-inline']       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-inline-end']   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-inline-start'] = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-left']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-right']        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-padding-top']          = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-snap-align']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['scroll-snap-stop']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['size']                        = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speak']                       = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speak-header']                = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speak-numeral']               = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speak-punctuation']           = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['speech-rate']                 = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['src']                         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['stress']                      = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['string-set']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['stroke']                      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['tab-size']                    = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['table-layout']                = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['target']                      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['target-name']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['target-new']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['target-position']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-align']                  = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-align-last']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration']             = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration-color']       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration-line']        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration-skip']        = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-decoration-style']       = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-emphasis']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-emphasis-color']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-emphasis-position']      = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-emphasis-style']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-height']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-indent']                 = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-justify']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-outline']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-shadow']                 = 'CSS2.0,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-space-collapse']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-transform']              = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-underline-position']     = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['text-wrap']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['top']                         = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transform']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transform-origin']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transform-style']             = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition-delay']            = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition-duration']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition-property']         = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['transition-timing-function']  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['unicode-bidi']                = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['vertical-align']              = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['visibility']                  = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-balance']               = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-duration']              = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-family']                = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-pitch']                 = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-pitch-range']           = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-rate']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-stress']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['voice-volume']                = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['volume']                      = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['white-space']                 = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['widows']                      = 'CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['width']                       = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['word-break']                  = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['word-spacing']                = 'CSS1.0,CSS2.0,CSS2.1,CSS3.0';
+$GLOBALS['csstidy']['all_properties']['word-wrap']                   = 'CSS3.0';
+$GLOBALS['csstidy']['all_properties']['z-index']                     = 'CSS2.0,CSS2.1,CSS3.0';
+
+/**
+ * An array containing all properties that can accept a quoted string as a value.
+ *
+ * @global array $GLOBALS['csstidy']['quoted_string_properties']
+ */
+$GLOBALS['csstidy']['quoted_string_properties'] = array( 'content', 'font', 'font-family', 'quotes' );
+
+/**
+ * An array containing all properties that can be defined multiple times without being overwritten.
+ * All unit values are included so that units like rem can be supported with fallbacks to px or em.
+ *
+ * @global array $GLOBALS['csstidy']['quoted_string_properties']
+ */
+$GLOBALS['csstidy']['multiple_properties'] = array_merge( $GLOBALS['csstidy']['color_values'], $GLOBALS['csstidy']['unit_values'], array( 'transition', 'background-image', 'border-image', 'list-style-image' ) );
+
+/**
+ * An array containing all predefined templates.
+ *
+ * @global array $GLOBALS['csstidy']['predefined_templates']
+ * @version 1.0
+ * @see csstidy::load_template()
+ */
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="at">'; // string before @rule
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span> <span class="format">{</span>' . "\n"; // bracket after @-rule
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="selector">'; // string before selector
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span> <span class="format">{</span>' . "\n"; // bracket after selector
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="property">'; // string before property
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span><span class="value">'; // string after property+before value
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span><span class="format">;</span>' . "\n"; // string after value
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="format">}</span>'; // closing bracket - selector
+$GLOBALS['csstidy']['predefined_templates']['default'][] = "\n\n"; // space between blocks {...}
+$GLOBALS['csstidy']['predefined_templates']['default'][] = "\n" . '<span class="format">}</span>' . "\n\n"; // closing bracket @-rule
+$GLOBALS['csstidy']['predefined_templates']['default'][] = ''; // indent in @-rule
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '<span class="comment">'; // before comment
+$GLOBALS['csstidy']['predefined_templates']['default'][] = '</span>' . "\n"; // after comment
+$GLOBALS['csstidy']['predefined_templates']['default'][] = "\n"; // after last line @-rule
+
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="at">';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span> <span class="format">{</span>' . "\n";
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="selector">';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="format">{</span>';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="property">';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="value">';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span><span class="format">;</span>';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="format">}</span>';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = "\n";
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = "\n" . '<span class="format">}' . "\n" . '</span>';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '';
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '<span class="comment">'; // before comment
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = '</span>'; // after comment
+$GLOBALS['csstidy']['predefined_templates']['high_compression'][] = "\n";
+
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="at">';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">{</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="selector">';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">{</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="property">';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="value">';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span><span class="format">;</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="format">}</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="format">}</span>';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '';
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '<span class="comment">'; // before comment
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '</span>'; // after comment
+$GLOBALS['csstidy']['predefined_templates']['highest_compression'][] = '';
+
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '<span class="at">';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span> <span class="format">{</span>' . "\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '<span class="selector">';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span>' . "\n" . '<span class="format">{</span>' . "\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '	<span class="property">';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span><span class="value">';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span><span class="format">;</span>' . "\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '<span class="format">}</span>';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = "\n\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = "\n" . '<span class="format">}</span>' . "\n\n";
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '	';
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '<span class="comment">'; // before comment
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = '</span>' . "\n"; // after comment
+$GLOBALS['csstidy']['predefined_templates']['low_compression'][] = "\n";
+
+require __DIR__ . '/data-wp.inc.php';
diff --git a/csstidy/lang.inc.php b/csstidy/lang.inc.php
new file mode 100644
index 0000000..08459f3
--- /dev/null
+++ b/csstidy/lang.inc.php
@@ -0,0 +1,303 @@
+<?php
+/**
+ * Localization of CSS Optimiser Interface of CSSTidy
+ *
+ * Copyright 2005, 2006, 2007 Florian Schmitz
+ *
+ * This file is part of CSSTidy.
+ *
+ *  CSSTidy is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published by
+ *  the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   CSSTidy is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ * @license https://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
+ * @package csstidy
+ * @author Florian Schmitz (floele at gmail dot com) 2005-2007, Brett Zamir (brettz9 at yahoo dot com) 2007
+ */
+
+if ( isset( $_GET['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes made to the site, determening language for translations.
+	$l = sanitize_text_field( wp_unslash( $_GET['lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes made to the site, determining language for translations.
+} elseif ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
+	$l = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
+	$l = strtolower( substr( $l, 0, 2 ) );
+} else {
+	$l = '';
+}
+
+$l = ( in_array( $l, array( 'de', 'fr', 'zh' ), true ) ) ? $l : 'en';
+
+// note 5 in all but French, and 40 in all are orphaned
+
+$lang           = array();
+$lang['en'][0]  = 'CSS Formatter and Optimiser/Optimizer (based on CSSTidy ';
+$lang['en'][1]  = 'CSS Formatter and Optimiser';
+$lang['en'][2]  = '(based on';
+$lang['en'][3]  = '(plaintext)';
+$lang['en'][4]  = 'Important Note:';
+$lang['en'][6]  = 'Your code should be well-formed. This is <strong>not a validator</strong> which points out errors in your CSS code. To make sure that your code is valid, use the <a href="https://jigsaw.w3.org/css-validator/">W3C Validator</a>.';
+$lang['en'][7]  = 'all comments are removed';
+$lang['en'][8]  = 'CSS Input:';
+$lang['en'][9]  = 'CSS-Code:';
+$lang['en'][10] = 'CSS from URL:';
+$lang['en'][11] = 'Code Layout:';
+$lang['en'][12] = 'Compression&#160;(code&#160;layout):';
+$lang['en'][13] = 'Highest (no readability, smallest size)';
+$lang['en'][14] = 'High (moderate readability, smaller size)';
+$lang['en'][15] = 'Standard (balance between readability and size)';
+$lang['en'][16] = 'Low (higher readability)';
+$lang['en'][17] = 'Custom (enter below)';
+$lang['en'][18] = 'Custom <a href="http://csstidy.sourceforge.net/templates.php">template</a>';
+$lang['en'][19] = 'Options';
+$lang['en'][20] = 'Sort Selectors (caution)';
+$lang['en'][21] = 'Sort Properties';
+$lang['en'][22] = 'Regroup selectors';
+$lang['en'][23] = 'Optimise shorthands';
+$lang['en'][24] = 'Compress colors';
+$lang['en'][25] = 'Lowercase selectors';
+$lang['en'][26] = 'Case for properties:';
+$lang['en'][27] = 'Lowercase';
+$lang['en'][28] = 'No or invalid CSS input or wrong URL!';
+$lang['en'][29] = 'Uppercase';
+$lang['en'][30] = 'lowercase elementnames needed for XHTML';
+$lang['en'][31] = 'Remove unnecessary backslashes';
+$lang['en'][32] = 'convert !important-hack';
+$lang['en'][33] = 'Output as file';
+$lang['en'][34] = 'Bigger compression because of smaller newlines (copy &#38; paste doesn\'t work)';
+$lang['en'][35] = 'Process CSS';
+$lang['en'][36] = 'Compression Ratio';
+$lang['en'][37] = 'Input';
+$lang['en'][38] = 'Output';
+$lang['en'][39] = 'Language';
+$lang['en'][41] = 'Attention: This may change the behavior of your CSS Code!';
+$lang['en'][42] = 'Remove last ;';
+$lang['en'][43] = 'Discard invalid properties';
+$lang['en'][44] = 'Only safe optimisations';
+$lang['en'][45] = 'Compress font-weight';
+$lang['en'][46] = 'Save comments';
+$lang['en'][47] = 'Do not change anything';
+$lang['en'][48] = 'Only separate selectors (split at ,)';
+$lang['en'][49] = 'Merge selectors with the same properties (fast)';
+$lang['en'][50] = 'Merge selectors intelligently (slow)';
+$lang['en'][51] = 'Preserve CSS';
+$lang['en'][52] = 'Save comments, hacks, etc. Most optimisations can *not* be applied if this is enabled.';
+$lang['en'][53] = 'None';
+$lang['en'][54] = 'Don\'t optimise';
+$lang['en'][55] = 'Safe optimisations';
+$lang['en'][56] = 'All optimisations';
+$lang['en'][57] = 'Add timestamp';
+$lang['en'][58] = 'Copy to clipboard';
+$lang['en'][59] = 'Back to top';
+$lang['en'][60] = 'Your browser doesn\'t support copy to clipboard.';
+$lang['en'][61] = 'For bugs and suggestions feel free to';
+$lang['en'][62] = 'contact me';
+$lang['en'][63] = 'Output CSS code as complete HTML document';
+$lang['en'][64] = 'Code';
+$lang['en'][65] = 'CSS to style CSS output';
+$lang['en'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
+
+$lang['de'][0]  = 'CSS Formatierer und Optimierer (basierend auf CSSTidy ';
+$lang['de'][1]  = 'CSS Formatierer und Optimierer';
+$lang['de'][2]  = '(basierend auf';
+$lang['de'][3]  = '(Textversion)';
+$lang['de'][4]  = 'Wichtiger Hinweis:';
+$lang['de'][6]  = 'Der CSS Code sollte wohlgeformt sein. Der CSS Code wird <strong>nicht auf Gültigkeit überprüft</strong>. Um sicherzugehen dass dein Code valide ist, benutze den <a href="https://jigsaw.w3.org/css-validator/">W3C Validierungsservice</a>.';
+$lang['de'][7]  = 'alle Kommentare werden entfernt';
+$lang['de'][8]  = 'CSS Eingabe:';
+$lang['de'][9]  = 'CSS-Code:';
+$lang['de'][10] = 'CSS von URL:';
+$lang['de'][11] = 'Code Layout:';
+$lang['de'][12] = 'Komprimierung&#160;(Code&#160;Layout):';
+$lang['de'][13] = 'Höchste (keine Lesbarkeit, niedrigste Größe)';
+$lang['de'][14] = 'Hoch (mittelmäßige Lesbarkeit, geringe Größe)';
+$lang['de'][15] = 'Standard (Kompromiss zwischen Lesbarkeit und Größe)';
+$lang['de'][16] = 'Niedrig (höhere Lesbarkeit)';
+$lang['de'][17] = 'Benutzerdefiniert (unten eingeben)';
+$lang['de'][18] = 'Benutzerdefinierte <a href="http://csstidy.sourceforge.net/templates.php">Vorlage</a>';
+$lang['de'][19] = 'Optionen';
+$lang['de'][20] = 'Selektoren sortieren (Vorsicht)';
+$lang['de'][21] = 'Eigenschaften sortieren';
+$lang['de'][22] = 'Selektoren umgruppieren';
+$lang['de'][23] = 'Shorthands optimieren';
+$lang['de'][24] = 'Farben komprimieren';
+$lang['de'][25] = 'Selektoren in Kleinbuchstaben';
+$lang['de'][26] = 'Groß-/Kleinschreibung für Eigenschaften';
+$lang['de'][27] = 'Kleinbuchstaben';
+$lang['de'][28] = 'Keine oder ungültige CSS Eingabe oder falsche URL!';
+$lang['de'][29] = 'Großbuchstaben';
+$lang['de'][30] = 'kleingeschriebene Elementnamen benötigt für XHTML';
+$lang['de'][31] = 'Unnötige Backslashes entfernen';
+$lang['de'][32] = '!important-Hack konvertieren';
+$lang['de'][33] = 'Als Datei ausgeben';
+$lang['de'][34] = 'Größere Komprimierung augrund von kleineren Neuezeile-Zeichen';
+$lang['de'][35] = 'CSS verarbeiten';
+$lang['de'][36] = 'Komprimierungsrate';
+$lang['de'][37] = 'Eingabe';
+$lang['de'][38] = 'Ausgabe';
+$lang['de'][39] = 'Sprache';
+$lang['de'][41] = 'Achtung: Dies könnte das Verhalten ihres CSS-Codes verändern!';
+$lang['de'][42] = 'Letztes ; entfernen';
+$lang['de'][43] = 'Ungültige Eigenschaften entfernen';
+$lang['de'][44] = 'Nur sichere Optimierungen';
+$lang['de'][45] = 'font-weight komprimieren';
+$lang['de'][46] = 'Kommentare beibehalten';
+$lang['de'][47] = 'Nichts ändern';
+$lang['de'][48] = 'Selektoren nur trennen (am Komma)';
+$lang['de'][49] = 'Selektoren mit gleichen Eigenschaften zusammenfassen (schnell)';
+$lang['de'][50] = 'Selektoren intelligent zusammenfassen (langsam!)';
+$lang['de'][51] = 'CSS erhalten';
+$lang['de'][52] = 'Kommentare, Hacks, etc. speichern. Viele Optimierungen sind dann aber nicht mehr möglich.';
+$lang['de'][53] = 'Keine';
+$lang['de'][54] = 'Nicht optimieren';
+$lang['de'][55] = 'Sichere Optimierungen';
+$lang['de'][56] = 'Alle Optimierungen';
+$lang['de'][57] = 'Zeitstempel hinzufügen';
+$lang['de'][58] = 'Copy to clipboard';
+$lang['de'][59] = 'Back to top';
+$lang['de'][60] = 'Your browser doesn\'t support copy to clipboard.';
+$lang['de'][61] = 'For bugs and suggestions feel free to';
+$lang['de'][62] = 'contact me';
+$lang['de'][63] = 'Output CSS code as complete HTML document';
+$lang['de'][64] = 'Code';
+$lang['de'][65] = 'CSS to style CSS output';
+$lang['de'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
+
+$lang['fr'][0]  = 'CSS Formatteur et Optimiseur (basé sur CSSTidy ';
+$lang['fr'][1]  = 'CSS Formatteur et Optimiseur';
+$lang['fr'][2]  = '(basé sur ';
+$lang['fr'][3]  = '(Version texte)';
+$lang['fr'][4]  = 'Note Importante&#160;:';
+$lang['fr'][6]  = 'Votre code doit être valide. Ce n’est <strong>pas un validateur</strong> qui signale les erreurs dans votre code CSS. Pour être sûr que votre code est correct, utilisez le validateur&#160;: <a href="https://jigsaw.w3.org/css-validator/">W3C Validator</a>.';
+$lang['fr'][7]  = 'tous les commentaires sont enlevés';
+$lang['fr'][8]  = 'Champ CSS&#160;:';
+$lang['fr'][9]  = 'Code CSS&#160;:';
+$lang['fr'][10] = 'CSS en provenance d’une URL&#160;:<br />';
+$lang['fr'][11] = 'Mise en page du code&#160;:';
+$lang['fr'][12] = 'Compression (mise en page du code)&#160;:';
+$lang['fr'][13] = 'La plus élevée (aucune lisibilité, taille minimale)';
+$lang['fr'][14] = 'Élevée (lisibilité modérée, petite taille)';
+$lang['fr'][15] = 'Normale (équilibre entre lisibilité et taille)';
+$lang['fr'][16] = 'Faible (lisibilité élevée)';
+$lang['fr'][17] = 'Sur mesure (entrer ci-dessous)';
+$lang['fr'][18] = '<a href="http://csstidy.sourceforge.net/templates.php">Gabarit</a> sur mesure';
+$lang['fr'][19] = 'Options';
+$lang['fr'][20] = 'Trier les sélecteurs (attention)';
+$lang['fr'][21] = 'Trier les propriétés';
+$lang['fr'][22] = 'Regrouper les sélecteurs';
+$lang['fr'][23] = 'Propriétés raccourcies';
+$lang['fr'][24] = 'Compresser les couleurs';
+$lang['fr'][25] = 'Sélecteurs en minuscules';
+$lang['fr'][26] = 'Case pour les propriétés&#160;:';
+$lang['fr'][27] = 'Minuscule';
+$lang['fr'][28] = 'CSS non valide ou URL incorrecte&#160;!';
+$lang['fr'][29] = 'Majuscule';
+$lang['fr'][30] = 'les noms des éléments en minuscules (indispensables pour XHTML)';
+$lang['fr'][31] = 'enlever les antislashs inutiles';
+$lang['fr'][32] = 'convertir !important-hack';
+$lang['fr'][33] = 'Sauver en tant que fichier';
+$lang['fr'][34] = 'Meilleure compression grâce aux caractères de saut de ligne plus petits (copier &#38; coller ne marche pas)';
+$lang['fr'][35] = 'Compresser la CSS';
+$lang['fr'][36] = 'Facteur de Compression';
+$lang['fr'][37] = 'Entrée';
+$lang['fr'][38] = 'Sortie';
+$lang['fr'][39] = 'Langue';
+$lang['fr'][41] = 'Attention&#160;: ceci peut changer le comportement de votre code CSS&#160;!';
+$lang['fr'][42] = 'Enlever le dernier ;';
+$lang['fr'][43] = 'Supprimer les propriétés non valide';
+$lang['fr'][44] = 'Seulement les optimisations sûres';
+$lang['fr'][45] = 'Compresser font-weight';
+$lang['fr'][46] = 'Sauvegarder les commentaires ';
+$lang['fr'][47] = 'Ne rien changer';
+$lang['fr'][48] = 'Sépare les sélecteurs (sépare au niveau de ,)';
+$lang['fr'][49] = 'Fusionne les sélecteurs avec les mêmes propriétés (rapide)';
+$lang['fr'][50] = 'Fusionne les sélecteurs intelligemment (lent)';
+$lang['fr'][51] = 'Préserver la CSS';
+$lang['fr'][52] = 'Sauvegarder les commentaires, hacks, etc. La plupart des optimisations ne peuvent *pas* être appliquées si cela est activé.';
+$lang['fr'][53] = 'Aucun';
+$lang['fr'][54] = 'Ne pas optimiser';
+$lang['fr'][55] = 'Optimisations sûres';
+$lang['fr'][56] = 'Toutes les optimisations';
+$lang['fr'][57] = 'Ajouter un timestamp';
+$lang['fr'][58] = 'Copier dans le presse-papiers';
+$lang['fr'][59] = 'Retour en haut';
+$lang['fr'][60] = 'Votre navigateur ne suporte pas la copie vers le presse-papiers.';
+$lang['fr'][61] = 'Pour signaler des bugs ou pour des suggestions,';
+$lang['fr'][62] = 'contactez-moi';
+$lang['fr'][63] = 'Sauver le code CSS comme document complet HTML';
+$lang['fr'][64] = 'Code';
+$lang['fr'][65] = 'CSS pour colorier la sortie CSS';
+$lang['fr'][66] = 'Vous devez aller dans about:config dans votre barre d’adresse, selectionner \'signed.applets.codebase_principal_support\' dans le champ Filtre et attribuez-lui la valeur \'true\' pour utiliser cette fonctionnalité; toutefois, soyez conscient que cela augmente les risques de sécurité.';
+
+$lang['zh'][0]  = 'CSS整形與最佳化工具(使用 CSSTidy ';
+$lang['zh'][1]  = 'CSS整形與最佳化工具';
+$lang['zh'][2]  = '(使用';
+$lang['zh'][3]  = '(純文字)';
+$lang['zh'][4]  = '重要事項:';
+$lang['zh'][6]  = '你的原始碼必須是良構的(well-formed). 這個工具<strong>沒有內建驗證器(validator)</strong>. 驗證器能夠指出你CSS原始碼裡的錯誤. 請使用 <a href="https://jigsaw.w3.org/css-validator/">W3C 驗證器</a>, 確保你的原始碼合乎規範.';
+$lang['zh'][7]  = '所有註解都移除了';
+$lang['zh'][8]  = 'CSS 輸入:';
+$lang['zh'][9]  = 'CSS 原始碼:';
+$lang['zh'][10] = 'CSS 檔案網址(URL):';
+$lang['zh'][11] = '原始碼規劃:';
+$lang['zh'][12] = '壓縮程度(原始碼規劃):';
+$lang['zh'][13] = '最高 (沒有辦法讀, 檔案最小)';
+$lang['zh'][14] = '高 (適度的可讀性, 檔案小)';
+$lang['zh'][15] = '標準 (兼顧可讀性與檔案大小)';
+$lang['zh'][16] = '低 (注重可讀性)';
+$lang['zh'][17] = '自訂 (在下方設定)';
+$lang['zh'][18] = '自訂<a href="http://csstidy.sourceforge.net/templates.php">樣板</a>';
+$lang['zh'][19] = '選項';
+$lang['zh'][20] = '整理選擇符(請謹慎使用)';
+$lang['zh'][21] = '整理屬性';
+$lang['zh'][22] = '重組選擇符';
+$lang['zh'][23] = '速記法(shorthand)最佳化';
+$lang['zh'][24] = '壓縮色彩語法';
+$lang['zh'][25] = '改用小寫選擇符';
+$lang['zh'][26] = '屬性的字形:';
+$lang['zh'][27] = '小寫';
+$lang['zh'][28] = '沒有輸入CSS, 語法不符合規定, 或是網址錯誤!';
+$lang['zh'][29] = '大寫';
+$lang['zh'][30] = 'XHTML必須使用小寫的元素名稱';
+$lang['zh'][31] = '移除不必要的反斜線';
+$lang['zh'][32] = '轉換 !important-hack';
+$lang['zh'][33] = '輸出成檔案形式';
+$lang['zh'][34] = '由於比較少換行字元, 會有更大的壓縮比率(複製&#38;貼上沒有用)';
+$lang['zh'][35] = '執行';
+$lang['zh'][36] = '壓縮比率';
+$lang['zh'][37] = '輸入';
+$lang['zh'][38] = '輸出';
+$lang['zh'][39] = '語言';
+$lang['zh'][41] = '注意: 這或許會變更你CSS原始碼的行為!';
+$lang['zh'][42] = '除去最後一個分號';
+$lang['zh'][43] = '拋棄不符合規定的屬性';
+$lang['zh'][44] = '只安全地最佳化';
+$lang['zh'][45] = '壓縮 font-weight';
+$lang['zh'][46] = '保留註解';
+$lang['zh'][47] = '什麼都不要改';
+$lang['zh'][48] = '只分開原本用逗號分隔的選擇符';
+$lang['zh'][49] = '合併有相同屬性的選擇符(快速)';
+$lang['zh'][50] = '聰明地合併選擇符(慢速)';
+$lang['zh'][51] = '保護CSS';
+$lang['zh'][52] = '保留註解與 hack 等等. 如果啟用這個選項, 大多數的最佳化程序都不會執行.';
+$lang['zh'][53] = '不改變';
+$lang['zh'][54] = '不做最佳化';
+$lang['zh'][55] = '安全地最佳化';
+$lang['zh'][56] = '全部最佳化';
+$lang['zh'][57] = '加上時間戳記';
+$lang['zh'][58] = '复制到剪贴板';
+$lang['zh'][59] = '回到页面上方';
+$lang['zh'][60] = '你的浏览器不支持复制到剪贴板。';
+$lang['zh'][61] = '如果程序有错误或你有建议,欢迎';
+$lang['zh'][62] = '和我联系';
+$lang['zh'][63] = 'Output CSS code as complete HTML document';
+$lang['zh'][64] = '代码';
+$lang['zh'][65] = 'CSS to style CSS output';
+$lang['zh'][66] = 'You need to go to about:config in your URL bar, select \'signed.applets.codebase_principal_support\' in the filter field, and set its value to true in order to use this feature; however, be aware that doing so increases security risks.';
diff --git a/csstidy/wordpress-standard.tpl b/csstidy/wordpress-standard.tpl
new file mode 100644
index 0000000..9499e83
--- /dev/null
+++ b/csstidy/wordpress-standard.tpl
@@ -0,0 +1,10 @@
+| {
+|| {
+|	| |;
+|}|
+
+|
+}
+
+|	||
+|
diff --git a/custom-css.php b/custom-css.php
new file mode 100644
index 0000000..f186afb
--- /dev/null
+++ b/custom-css.php
@@ -0,0 +1,73 @@
+<?php
+/*
+ * Plugin Name: A/I - Sanitize Custom CSS
+ * Description: Sanitize Custom CSS by preventing url() references
+ * Version: 0.0.1
+ * Author: Autistici/Inventati
+ * Author URI: https://autistici.org
+ */
+
+// The following code is heavily inspired by the Jetpack plugin custom-css module.
+function ai_sanitize_css( $css, $args = array() ) {
+    require_once(__DIR__ . '/csstidy/class.csstidy.php');
+
+    $args = wp_parse_args(
+        $args,
+        array(
+            'force'        => false,
+        )
+    );
+	
+    if ($args['force'] || !current_user_can('unfiltered_html')) {
+        $warnings = array();
+	
+        safecss_class();
+        $csstidy           = new csstidy();
+        $csstidy->optimise = new safecss( $csstidy );
+
+        $csstidy->set_cfg( 'remove_bslash', false );
+        $csstidy->set_cfg( 'compress_colors', false );
+        $csstidy->set_cfg( 'compress_font-weight', false );
+        $csstidy->set_cfg( 'optimise_shorthands', 0 );
+        $csstidy->set_cfg( 'remove_last_;', false );
+        $csstidy->set_cfg( 'case_properties', false );
+        $csstidy->set_cfg( 'discard_invalid_properties', true );
+        $csstidy->set_cfg( 'css_level', 'CSS3.0' );
+        $csstidy->set_cfg( 'preserve_css', true );
+        $csstidy->set_cfg( 'template', __DIR__ . '/csstidy/wordpress-standard.tpl' );
+
+        // Test for some preg_replace stuff.
+        $prev = $css;
+        $css  = preg_replace( '/\\\\([0-9a-fA-F]{4})/', '\\\\\\\\$1', $css );
+        // prevent content: '\3434' from turning into '\\3434'.
+        $css = str_replace( array( '\'\\\\', '"\\\\' ), array( '\'\\', '"\\' ), $css );
+        if ( $css !== $prev ) {
+            $warnings[] = 'preg_replace found stuff';
+        }
+
+        // Some people put weird stuff in their CSS, KSES tends to be greedy.
+        $css = str_replace( '<=', '&lt;=', $css );
+
+        // Test for some kses stuff.
+        $prev = $css;
+        // Why KSES instead of strip_tags?  Who knows?
+        $css = wp_kses_split( $css, array(), array() );
+        $css = str_replace( '&gt;', '>', $css ); // kses replaces lone '>' with &gt;
+        // Why both KSES and strip_tags?  Because we just added some '>'.
+        $css = strip_tags( $css ); // phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags -- scared to update this to wp_strip_all_tags since we're building a CSS file here.
+
+        if ( $css !== $prev ) {
+            $warnings[] = 'kses found stuff';
+        }
+
+        $csstidy->parse( $css );
+        $css = $csstidy->print->plain();
+    }
+    return $css;
+}
+
+add_filter('update_custom_css_data', function($args) {
+    $css = $args['css'];
+    $args['css'] = ai_sanitize_css($css);
+    return $args;
+}, 10, 2);
-- 
GitLab