Location: PHPKode > scripts > CSS3 Maximizer > CSS-Maximizer-master/CSS3Maximizer.php
<?php

/*

	CSS3Maximizer : v0.1 : mudcu.be
	------------------------------------
	Adds compatibility for vendors through proprietary CSS properties.  No hassle!
	Use your favorite syntax and CSS3Maximizer fills in the holes.


	CSS3 Color Module
	------------------
	#00FF00 // all browsers
	hsl(120, 100%, 50%); //
	hsla(120, 100%, 50%, 1); //
	rgb(0, 255, 0); //
	rgb(0, 100%, 0); //
	rgba(0, 255, 0%, 1); //
	rgba(0, 100%, 0%, 1); //


	CSS3 Gradient Module
	---------------------
	linear-gradient(yellow, blue);
	linear-gradient(to top, blue, yellow);
	linear-gradient(180deg, yellow, blue);
	linear-gradient(to bottom, yellow 0%, blue 100%);
	-webkit-gradient(linear, left top, left bottom, color-stop(0%, #444444), color-stop(100%, #999999)); // Saf4+, Chrome
	-webkit-gradient(linear, left top, left bottom, from(#444444), to(#999999)); // Saf4+, Chrome
	-webkit-linear-gradient(top, #444444, #999999); // Chrome 10+, Saf5.1+
	-moz-linear-gradient(top, #444444, #999999); // FF3.6
	-ms-linear-gradient(top, #444444, #999999); // IE10
	-o-linear-gradient(top, #444444, #999999); // Opera 11.10+
	filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#444444', EndColorStr='#999999'); // IE6–IE9


	CSS3 Properties
	----------------
	background-clip
	----------------
		   -moz-background-clip: padding;
		-webkit-background-clip: padding-box;
				background-clip: padding-box;

	background-size
	----------------
		   -moz-background-size: 100% 100%; // FF3.6
		-webkit-background-size: 100% 100%; // Saf3-4
				background-size: 100% 100%; // Opera, IE9, Saf5, Chrome, FF4

	border-radius
	--------------
			 -moz-border-radius: 12px; // FF1-3.6
		  -webkit-border-radius: 12px; // Saf3-4, iOS 1-3.2, Android <1.6
				  border-radius: 12px; // Opera 10.5, IE9, Saf5, Chrome, FF4, iOS 4, Android 2.1+

	box-shadow
	-----------
				-moz-box-shadow: 0px 0px 4px #ffffff; // FF3.5 - 3.6
			 -webkit-box-shadow: 0px 0px 4px #ffffff; // Saf3.0+, Chrome
					 box-shadow: 0px 0px 4px #ffffff; // Opera 10.5, IE9, FF4+, Chrome 10+

	transition
	-----------
				-moz-transition: all 0.3s ease-out;  // FF4+
				  -o-transition: all 0.3s ease-out;  // Opera 10.5+
			 -webkit-transition: all 0.3s ease-out;  // Saf3.2+, Chrome
				 -ms-transition: all 0.3s ease-out;  // IE10?
					 transition: all 0.3s ease-out;

	transform
	----------
				 -moz-transform: rotate(7.5deg);  // FF3.5+
				   -o-transform: rotate(7.5deg);  // Opera 10.5
			  -webkit-transform: rotate(7.5deg);  // Saf3.1+, Chrome
				  -ms-transform: rotate(7.5deg);  // IE9
					  transform: rotate(7.5deg);
	user-select
	------------
					user-select: none;
			 -khtml-user-select: none;
			   -moz-user-select: none;
				 -o-user-select: none;
			-webkit-user-select: none;


	TODO
	-----
	Maximize filter "opacity" for Internet Explorer:
		opacity: 0 === filter: alpha(opacity=0);

*/

class CSS3Maximizer {
	private $ColorSpace;
	private $code;

	public function __construct() {
		$this->ColorSpace = new ColorSpace;
	}

	/* Direct conversions of properties between vendors */

	private $defAlias = Array(
		"background-clip" => Array(
			"background-clip",
			"-moz-background-clip",
			"-webkit-background-clip"
		),
		"background-size" => Array(
			"background-size",
			"-moz-background-size",
			"-webkit-background-size"
		),
		"border-radius" => Array(
			"border-radius",
			"-moz-border-radius",
			"-webkit-border-radius"
		),
		"box-shadow" => Array(
			"box-shadow",
			"-moz-box-shadow",
			"-webkit-box-shadow"
		),
		"text-shadow" => Array(
			"text-shadow",
			"-moz-text-shadow"
		),
		"transition" => Array(
			"transition",
			"-moz-transition",
			"-ms-transition",
			"-o-transition",
			"-webkit-transition"
		),
		"transition-property" => Array(
			"transition-property",
			"-moz-transition-property",
			"-ms-transition-property",
			"-o-transition-property",
			"-webkit-transition-property"
		),
		"transition-duration" => Array(
			"transition-duration",
			"-moz-transition-duration",
			"-ms-transition-duration",
			"-o-transition-duration",
			"-webkit-transition-duration",
		),
		"transform" => Array(
			"transform",
			"-moz-transform",
			"-ms-transform",
			"-o-transform",
			"-webkit-transform"
		),
		"user-select" => Array(
			"user-select",
			"-khtml-user-select",
			"-moz-user-select",
			"-o-user-select",
			"-webkit-user-select"
		)
	);

	private $defStaticProps = Array(
		"0",
		"none",
		"transparent",
		"inherit"
	);

	// Color Properties

	private $defColorFallback = Array(
		"color", // single value, rgba with hex fallback
		"background-color", // single value, rgba with hex fallback
		"border", // multiple values, rgba with hex fallback
		"border-color" // multiple values, rgba with hex fallback
	);

	private $defColorProperties = Array(
		"color", // single value, rgba with hex fallback
		"background-color", // single value, rgba with hex fallback
		"border", // multiple values, rgba with hex fallback
		"border-color", // multiple values, rgba with hex fallback
		"box-shadow", // multiple values, can always use rgba
		"text-shadow" // multiple values, can always use rgba
	);

	// Gradient Properties

	private $defGradientProperties = Array(
		"background",
		"background-image"
	);

	private $defGradientLinear = Array(
		"-webkit-gradient",
		"-webkit-linear-gradient",
		"-moz-linear-gradient",
		"-ms-linear-gradient",
		"-o-linear-gradient",
		"linear-gradient",
		"filter"
	);

	/* Color parsing and standardization */

	private function splitByColor($value) {
		$values = Array();
		while(strlen($value)) {
			$ishex = strpos($value, "#");
			$isother = strpos($value, ")");
			if ($ishex === false) $ishex = 99999999999;
			if ($isother === false) $isother = 99999999999;
			if ($ishex < $isother) {
				$comma = strpos($value, ",");
				if ($comma === false) { // end of the line
					array_push($values, $value);
					$value = "";
				} else { // split by comma [hex]
					array_push($values, substr($value, 0, $comma));
					$value = trim(substr($value, $comma + 1));
				}
			} else { // split by comma [rgb, rgba, hsl, hsla]
				array_push($values, substr($value, 0, $isother + 1));
				$value = trim(substr($value, $isother + 2));
			}
		}
		return $values;
	}

	private function parseColors($colors, $doFallback = false) {
		$fallback = Array();
		foreach ($colors as $key => $value) {
			$tmp = str_replace("  ", " ", trim($value));
			if (in_array($tmp, $this->defStaticProps)) { // none, inherit, transparent
				return $tmp;
			}
			$pos = strpos($tmp, "(");
			if ($pos === false) { // split color from properties [hex]
				$pos = strpos($tmp, "#");
				$first = substr($tmp, 0, $pos);
				$color = substr($tmp, $pos);
			} else { // split color from properties [rgb, rgba, hsl or hsla]
				$pos = substr($tmp, 0, $pos);
				$backpos = strrpos($pos, " ");
				if ($backpos === false) $backpos = -1;
				$first = substr($pos, 0, $backpos + 1);
				$color = substr($tmp, $backpos + 1);
			}
			$color = $this->parseColor($color);
			if ($doFallback) { // include hex fallback when alpha is present
				if ($color["rgba"]) {
					$fallback[$key] = $first . $color["hex"];
					$colors[$key] = $first . $color["rgba"];
				} else {
					$fallback[$key] = $first . $color["hex"];
					$colors[$key] = $first . $color["hex"];
				}
			} else if ($color["rgba"]) {
				$colors[$key] = $first . $color["rgba"];
			} else { // everything is supported in hex!
				$colors[$key] = $first . $color["hex"];
			}
		}
		$colors = implode($colors, ", ");
		$fallback = implode($fallback, ", ");
		if ($doFallback && $colors !== $fallback) { // include fallback
			return Array(
				"hex" => $fallback,
				"rgba" => $colors
			);
		} else { // no fallback necessary
			return $colors;
		}
	}

	private function parseColor($color) {
		$color = trim($color);
		if (strpos($color, "(")) { // rgb, rgba, hsl or hsla
			$first = strpos($color, "(");
			$type = substr($color, 0, $first);
			$color = substr($color, $first + 1, -1);
			$color = explode(",", $color);
			$alpha = isset($color[3]) ? floatval($color[3]) : 1;
			switch ($type) { // regularize to rgba and hex
				case "rgb":
				case "rgba":
					if (strpos($color[0], "%")) {
						$color[0] = round(intval($color[0]) / 100 * 255);
						$color[1] = round(intval($color[1]) / 100 * 255);
						$color[2] = round(intval($color[2]) / 100 * 255);
					}
					$color = Array(
						R => $color[0],
						G => $color[1],
						B => $color[2]
					);
					break;
				case "hsl": // convert to rgb()
				case "hsla": // convert to rgba()
					$color = $this->ColorSpace->HSL_RGB(Array(
						H => $color[0],
						S => $color[1],
						L => $color[2]
					));
					break;
				default: // hex
					break;
			}
			$hex = "#" . $this->ColorSpace->HEX_STRING($this->ColorSpace->RGB_HEX($color));
			if ($alpha === 1) {
				return Array("hex" => $hex);
			} else { // requires alpha
				$r = max(0, min(255, round($color[R])));
				$g = max(0, min(255, round($color[G])));
				$b = max(0, min(255, round($color[B])));
				return Array(
					"rgba" => "rgba(".$r.", ".$g.", ".$b.", ".$alpha.")",
					"hex" => $hex
				);
			}
		} else {
			return Array(
				"hex" => $color
			);
		}
	}

	/* Gradient parsing and standardization */

	private function splitGradient($value) {
		$values = Array();
		while(strlen($value)) {
			$ishex = strpos($value, ",");
			$isother = strpos($value, "(");
			if ($ishex === false) $ishex = 99999999999;
			if ($isother === false) $isother = 99999999999;
			if ($ishex < $isother) {
				$comma = strpos($value, ",");
				array_push($values, substr($value, 0, $comma));
				$value = trim(substr($value, $comma + 1));
			} else { // split by comma [rgb, rgba, hsl, hsla]
				$isother = strpos($value, ")");
				if ($isother === false) {
					array_push($values, $value);
					$value = "";
				} else {
					$stop = substr($value, 0, $isother + 1);
					if (substr_count($stop, '(') === 2) {
						$isother += 1;
						$stop = $stop . ")";
					}
					array_push($values, $stop);
					$value = trim(substr($value, $isother + 2));
				}
			}
		}
		return $values;
	}

	private function Webkit_Gradient_Position($value) {
		switch($value) {
			case "top":
				return Array( "y" => -1 );
			case "left":
				return Array( "x" => -1 );
			case "bottom":
				return Array( "y" => 1 );
			case "right":
				return Array( "x" => 1 );
			default: // center
				return Array();
		}
	}

	private function Webkit_to_W3C_Gradient($value) {

		///--- webkit supports out-of-order color-stops (others fail)...

		array_shift($value); // type of gradient [assume linear]
		$start = explode(" ", array_shift($value));
		$end = explode(" ", array_shift($value));
		$aIsSame = $start[0] == $end[0];
		$bIsSame = $start[1] == $end[1];
		if ($aIsSame && !$bIsSame) {
			$start = "top";
		} else if (!$aIsSame && $bIsSame) {
			$start = "left";
		} else if (!$aIsSame && !$bIsSame) { // convert to angle
			$p1 = array_merge(
				Array( "x" => 0, "y" => 0 ),
				$this->Webkit_Gradient_Position($start[0]),
				$this->Webkit_Gradient_Position($start[1])
			);
			$p2 = array_merge(
				Array( "x" => 0, "y" => 0 ),
				$this->Webkit_Gradient_Position($end[0]),
				$this->Webkit_Gradient_Position($end[1])
			);
			$dy = $p2[y] - $p1[y];
			$dx = $p2[x] - $p1[x];
			$start = round(rad2deg(atan2($dy, $dx))) . "deg";
		} else { // is "left"
			$start = "left";
		}
		$values = Array();
		$moz = Array();
		//
		foreach ($value as $key) {
			$type = substr($key, 0, strpos($key, "("));
			$key = substr($key, strpos($key, "(") + 1);
			if ($type == "from") {
				$position = "0%";
				$color = substr($key, 0, -1);
			} else if ($type == "to") {
				$position = "100%";
				$color = substr($key, 0, -1);
			} else {
				$key = explode(",", $key, 2);
				$position = $key[0];
				if (!strpos($position, "%")) {
					$position = round($position * 100) . "%";
				}
				$color = substr($key[1], 0, -1);
			}
			$color = $this->parseColor($color);
			if ($color["rgba"]) {
				array_push($values, $color["rgba"] . " " . $position);
			} else {
				array_push($values, $color["hex"] . " " . $position);
			}
			array_push($moz, $color["hex"] . " " . $position);
		}
		return Array(
			"microsoft" => Array( substr(reset($moz),0,7), substr(end($moz),0,7) ),
			"moz" => $start . ", " . implode($moz, ", "),
			"w3c" => $start . ", " . implode($values, ", ")
		);
	}

	private function W3C_to_Webkit_Gradient($value) {
		$start = array_shift($value);
		switch ($start) {
			case "top":
				$start = "center top, center bottom, ";
				break;
			case "left":
				$start = "left center, right center, ";
				break;
			default: // angle
				$start = deg2rad(intval($start));
				$x = round(cos($start) * 100);
				$y = round(sin($start) * 100);
				$start = $x . "% 0%, 0% " . $y . "%, ";
				break;
		}
		$count = count($value) - 1;
		$values = Array();
		foreach ($value as $n => $key) {
			$key = explode(" ", $key);
			$color = $this->parseColor($key[0]);
			if ($color["rgba"]) {
				$color = $color["rgba"];
			} else {
				$color = $color["hex"];
			}
			$position = $key[1];
			if (gettype($position) == "NULL") {
				$position = round($n / $count * 100) . '%';
			}
			if ($n === 0) {
				$first = $color;
				array_push($values, "from({$color})");
			} else if ($n === $count) {
				$last = $color;
				array_push($values, "to({$color})");
			} else {
				array_push($values, "color-stop({$position}, {$color})");
			}
		}
		return Array(
			"microsoft" => Array( $first, $last ),
			"webkit" => "linear, " . $start . implode($values, ", ")
		);
	}

	private function parseGradient($property, $value) {
		$type = substr($value, 0, strpos($value, "("));
		$tmp = substr($value, strpos($value, "(") + 1, -1);
		$values = Array();
		if ($type == "-webkit-gradient") { // convert from webkit to other
			$value = $this->Webkit_to_W3C_Gradient($this->splitGradient($tmp));
			$value["webkit"] = $tmp;
		} else { // convert from other to webkit
			$value = $this->W3C_to_Webkit_Gradient($this->splitGradient($tmp));
			$value["w3c"] = $tmp;
		}
		foreach ($this->defGradientLinear as $key) {
			if ($key == "-webkit-gradient") {
				$values[$key] = $key . "(" . $value["webkit"] . ")";
			} else if ($key == "filter") {
				$color = $value["microsoft"];
				$values[$key] = "filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='{$color[0]}', EndColorStr='{$color[1]}')";
			} else {
				$values[$key] = $key . "(" . $value["w3c"] . ")";
			}
		}
		return $values;
	}

	/* Convert CSS to Object */

	private function ParseCSS($str) {
		$css = Array();
		$str = preg_replace("/\/\*(.*)?\*\//Usi", "", $str);
		$parts = explode("}", $str);
		$skipping = false;
		if (count($parts) > 0) {
			foreach($parts as $part) {
				list($keystr, $codestr) = explode("{", $part);
				// @media queries [skip for now]
				if ($skipping) {
					if (substr_count($part, '{') === 0) {
						$skipping = false;
						$css[$id[0]] .= "}\r";
					} else {
						$css[$id[0]] .= $part . "}\r";
						continue;
					}
				}
				if (substr(trim($part), 0, 1) === "@") {
					$id = explode(",", trim($keystr));
					$css[$id[0]] = trim($part) . "\r\t}";
					$skipping = true;
					continue;
				}
				// everything else
				$keys = explode(",", trim($keystr));
				if (count($keys) === 0) continue;
				foreach($keys as $key) {
					if (strlen($key) === 0) continue;
					$key = str_replace("\n", "", $key);
					$key = str_replace("\\", "", $key);
					$codestr = trim($codestr);
					if (!isset($css[$key])) {
						$css[$key] = array();
					}
					// only match ; without surrounding quotes
					$codestr = preg_replace('/(?:["\'].*(;).*["\'])/e', 'str_replace(";","{{{RETAIN_SEPERATOR}}}","$0")', $codestr);
					$codes = explode(";", $codestr);
					if (count($codes) === 0) continue;
					foreach($codes as $code) {
						// put back ; which were within surrounding quotes
						$code = str_replace("{{{RETAIN_SEPERATOR}}}", ";", $code);
						$code = trim($code);
						list($codekey, $codevalue) = explode(":", $code, 2);
						if (strlen($codekey) === 0) continue;
						array_push($css[$key], Array(
							"type" => trim($codekey),
							"value" => trim($codevalue))
						);
					}
				}
			}
		}
		return $css;
	}

	/* Generate compatibility between vendors */

	public function clean($config) {
		$css = isset($config['css']) ? $config['css'] : '';
		$url = isset($config['url']) ? $config['url'] : '';
		$compress = isset($config['compress']) ? $config['compress'] : '';
		// load from file and write file
		if (strpos($css, ".css") && is_file($css)) {
			$this->code = file_get_contents($css);
		} else {
			$this->code = $css;
		}

		$cssObject = Array();
		$cssText = "";
		$css = $this->ParseCSS($this->code);
		// run through properties and add appropriate compatibility
		foreach ($css as $cssID => $cssProperties) {
			$properties = Array();
			if (gettype($cssProperties) === "string") {
				$cssObject[$cssID] = $cssProperties;
				continue;
			}
			foreach ($cssProperties as $value) {
				$type = $value["type"];
				$value = $value["value"];
				if(in_array($type, $this->defGradientProperties)) {
					if (substr(trim($value), 0, 4) === "url(") {
						$value = substr(trim($value), 5, -1);
						$value = str_replace(Array("'",'"'), Array(''), $value);
						$value = 'url("' . $url . $value . '")';
					} else if (strpos($value, "gradient") !== false) { // background-gradient
						$value = $this->parseGradient($type, $value);
					} else { // background-color as "background"
						$doFallback = in_array($type, $this->defColorFallback);
						$value = $this->parseColors($this->splitByColor($value), $doFallback);
					}
				} else if (in_array($type, $this->defColorProperties)) {
					$doFallback = in_array($type, $this->defColorFallback);
					$value = $this->parseColors($this->splitByColor($value), $doFallback);
				}
				$alias = Array();
				foreach ($this->defAlias as $property) {
					if (in_array($type, $property)) { // direct conversion between vendors
						foreach ($property as $key) {
							if ($key == "-moz-transition") {
								$tmp = explode(" ", $value);
								if ($tmp[0] == "transform") $tmp[0] = "-moz-transform";
								$tmp = implode($tmp, " ");
								$alias[$key] = $tmp;
							} else if ($key == "-moz-transition-property") {
								$tmp = $value;
								if ($tmp == "transform") $tmp = "-moz-transform";
								$alias[$key] = $tmp;
							} else {
								$alias[$key] = $value;
							}
						}
					}
				}
				if (count($alias)) {
					$value = $alias;
				}
				$merged = false;
				foreach ($properties as $key => $property) {
					$typeof = gettype($property["value"]);
					if ($property["type"] == $type && gettype($value) === "array") {
						if ($typeof == "string") {
							$property["value"] = Array(
								"hex" => $property["value"]
							);
						}
						$properties[$key]["value"] = array_merge(
							$property["value"],
							$value
						);
						$merged = true;
					} else if ($typeof == "array" && $property["value"][$type]) {
						if ($type === "filter") {
							$value = Array(
								"filter" => $type.": ".$value
							);
						}
						$properties[$key]["value"] = array_merge(
							$property["value"],
							$value
						);
						$merged = true;
					} else {

					}
				}
				if ($merged === false) {
					array_push($properties, Array(
						"type" => $type,
						"value" => $value
					));
				}
			}
			$cssObject[$cssID] = $properties;
		}
		$newline = $compress ? "" : "\n";
		$space = $compress ? "" : " ";
		$tab = $compress ? "" : "\t";
		// composite $cssObject into $cssText
		$cssArray = Array();
		foreach ($cssObject as $cssID => $cssProperties) {
			if (gettype($cssProperties) === "string") {
				$cssText = substr($cssProperties, strpos($cssProperties, "{")+1);
				$cssText = "\t" . trim(substr($cssText, 0, strrpos($cssText, "}"))) . "\n";
				array_push($cssArray, Array(
					"text" => $cssText,
					"key" => $cssID
				));
				continue;
			}
			$cssText = "";
			foreach ($cssProperties as $value) {
				$type = $value["type"];
				$value = $value["value"];
				if (gettype($value) == "string") { // general properties
					if ($compress) $value = str_replace(", ", ",", $value);
					$value = str_replace(Array('\"',"\'"), Array('"',"'"), $value);
					$cssText .= $tab . $type . ":{$space}" . $value . ";{$newline}";
				} else { // multiple values
					foreach ($value as $key => $tmp) {
						if ($compress) $tmp = str_replace(", ", ",", $tmp);
						$tmp = str_replace(Array('\"',"\'"), Array('"',"'"), $tmp);
						if ($key == "hex" || $key == "rgba" || in_array($key, $this->defGradientLinear)) { // color or gradient variants
							if ($key == "filter") { // microsoft values
								$cssText .= $tab . $tmp . ";{$newline}";
							} else {
								$cssText .= $tab . $type . ":{$space}" . $tmp . ";{$newline}";
							}
						} else { // direct conversion of vender variants
							$cssText .= $tab . $key . ":{$space}" . $tmp . ";{$newline}";
						}
					}
				}
			}
			array_push($cssArray, Array(
				"text" => $cssText,
				"key" => $cssID
			));
		}
		$cssText = "";
		foreach ($cssArray as $n => $value) {
			$cssID = $value["key"];
			$content1 = $cssArray[$n]["text"];
			$content2 = $cssArray[$n + 1]["text"];
			if ($content1 === $content2) {
				$cssText .= $cssID . $space . "," . $newline;
			} else {
				$cssText .= $cssID . $space . "{" . $newline;
				$cssText .= $content1;
				$cssText .= "}" . $newline;
			}
		}

		return $cssText;
	}
};

?>
Return current item: CSS3 Maximizer