* @copyright 2008 - 2010 Joe Scylla * @license http://opensource.org/licenses/mit-license.php MIT License * @version 2.0.1.0064 (2010-09-30) */ class CssMin { /** * State: Is in document * * @var integer */ const T_DOCUMENT = 1; /** * Token: Comment * * @var integer */ const T_COMMENT = 2; /** * Token: Generic at-rule * * @var integer */ const T_AT_RULE = 3; /** * Token: Start of @media block * * @var integer */ const T_AT_MEDIA_START = 4; /** * State: Is in @media block * * @var integer */ const T_AT_MEDIA = 5; /** * Token: End of @media block * * @var integer */ const T_AT_MEDIA_END = 6; /** * Token: Start of @font-face block * * @var integer */ const T_AT_FONT_FACE_START = 7; /** * State: Is in @font-face block * * @var integer */ const T_AT_FONT_FACE = 8; /** * Token: @font-face declaration * * @var integer */ const T_FONT_FACE_DECLARATION = 9; /** * Token: End of @font-face block * * @var integer */ const T_AT_FONT_FACE_END = 10; /** * Token: Start of @page block * * @var integer */ const T_AT_PAGE_START = 11; /** * State: Is in @page block * * @var integer */ const T_AT_PAGE = 12; /** * Token: @page declaration * * @var integer */ const T_PAGE_DECLARATION = 13; /** * Token: End of @page block * * @var integer */ const T_AT_PAGE_END = 14; /** * Token: Start of ruleset * * @var integer */ const T_RULESET_START = 15; /** * Token: Ruleset selectors * * @var integer */ const T_SELECTORS = 16; /** * Token: Start of declarations * * @var integer */ const T_DECLARATIONS_START = 17; /** * State: Is in declarations * * @var integer */ const T_DECLARATIONS = 18; /** * Token: Declaration * * @var integer */ const T_DECLARATION = 19; /** * Token: End of declarations * * @var integer */ const T_DECLARATIONS_END = 20; /** * Token: End of ruleset * * @var integer */ const T_RULESET_END = 21; /** * Token: Start of @variables block * * @var integer */ const T_AT_VARIABLES_START = 100; /** * State: Is in @variables block * * @var integer */ const T_AT_VARIABLES = 101; /** * Token: @variables declaration * * @var integer */ const T_VARIABLE_DECLARATION = 102; /** * Token: End of @variables block * * @var integer */ const T_AT_VARIABLES_END = 103; /** * State: Is in string * * @var integer */ const T_STRING = 254; /** * State: Is in url string property * * @var integer */ const T_STRING_URL = 255; /** * Css transformations table * * @var array */ private static $transformations = array ( "border-radius" => array("-moz-border-radius", "-webkit-border-radius", "-khtml-border-radius"), "border-top-left-radius" => array("-moz-border-radius-topleft", "-webkit-border-top-left-radius", "-khtml-top-left-radius"), "border-top-right-radius" => array("-moz-border-radius-topright", "-webkit-border-top-right-radius", "-khtml-top-right-radius"), "border-bottom-right-radius" => array("-moz-border-radius-bottomright", "-webkit-border-bottom-right-radius", "-khtml-border-bottom-right-radius"), "border-bottom-left-radius" => array("-moz-border-radius-bottomleft", "-webkit-border-bottom-left-radius", "-khtml-border-bottom-left-radius"), "box-shadow" => array("-moz-box-shadow", "-webkit-box-shadow", "-khtml-box-shadow"), "opacity" => array(array("CssMin", "_tOpacity")), "text-shadow" => array("-moz-text-shadow", "-webkit-text-shadow", "-khtml-text-shadow"), "white-space" => array(array("CssMin", "_tWhiteSpacePreWrap")) ); /** * Minifies the Css. * * @param string $css * @param array $config [optional] * @return string */ public static function minify($css, $config = array()) { $tokens = self::parse($css); $config = array_merge(array ( "remove-empty-blocks" => true, "remove-empty-rulesets" => true, "remove-last-semicolons" => true, "convert-css3-properties" => false, "convert-color-values" => false, "compress-color-values" => false, "compress-unit-values" => false, "emulate-css3-variables" => true, ), $config); // Minification options $sRemoveEmptyBlocks = $config["remove-empty-blocks"]; $sRemoveEmptyRulesets = $config["remove-empty-rulesets"]; $sRemoveLastSemicolon = $config["remove-last-semicolons"]; $sConvertCss3Properties = $config["convert-css3-properties"]; $sCompressUnitValues = $config["compress-unit-values"]; $sConvertColorValues = $config["convert-color-values"]; $sCompressColorValues = $config["compress-color-values"]; $sEmulateCcss3Variables = $config["emulate-css3-variables"]; $sRemoveTokens = array(self::T_COMMENT); // Remove tokens if (!$sEmulateCcss3Variables) { $sRemoveTokens = array_merge($sRemoveTokens, array(self::T_AT_VARIABLES_START, self::T_VARIABLE_DECLARATION, self::T_AT_VARIABLES_END)); } for($i = 0, $l = count($tokens); $i < $l; $i++) { if (in_array($tokens[$i][0], $sRemoveTokens)) { unset($tokens[$i]); } } $tokens = array_values($tokens); // Remove empty rulesets if ($sRemoveEmptyRulesets) { for($i = 0, $l = count($tokens); $i < $l; $i++) { // Remove empty rulesets if ($tokens[$i][0] == self::T_RULESET_START && $tokens[$i+4][0] == self::T_RULESET_END) { unset($tokens[$i]); // T_RULESET_START unset($tokens[++$i]); // T_SELECTORS unset($tokens[++$i]); // T_DECLARATIONS_START unset($tokens[++$i]); // T_DECLARATIONS_END unset($tokens[++$i]); // T_RULESET_END } } $tokens = array_values($tokens); } // Remove empty @media, @font-face or @page blocks if ($sRemoveEmptyBlocks) { for($i = 0, $l = count($tokens); $i < $l; $i++) { // Remove empty @media, @font-face or @page blocks if (($tokens[$i][0] == self::T_AT_MEDIA_START && $tokens[$i+1][0] == self::T_AT_MEDIA_END) || ($tokens[$i][0] == self::T_AT_FONT_FACE_START && $tokens[$i+1][0] == self::T_AT_FONT_FACE_END) || ($tokens[$i][0] == self::T_AT_PAGE_START && $tokens[$i+1][0] == self::T_AT_PAGE_END)) { unset($tokens[$i]); // T_AT_MEDIA_START, T_AT_FONT_FACE_START, T_AT_PAGE_START unset($tokens[++$i]); // T_AT_MEDIA_END, T_AT_FONT_FACE_END, T_AT_PAGE_END } } $tokens = array_values($tokens); } // CSS Level 3 variables: parse variables if ($sEmulateCcss3Variables) { // Parse variables $variables = array(); for($i = 0, $l = count($tokens); $i < $l; $i++) { if ($tokens[$i][0] == self::T_VARIABLE_DECLARATION) { for($i2 = 0, $l2 = count($tokens[$i][3]); $i2 < $l2; $i2++) { if (!isset($variables[$tokens[$i][3][$i2]])) { $variables[$tokens[$i][3][$i2]] = array(); } $variables[$tokens[$i][3][$i2]][$tokens[$i][1]] = $tokens[$i][2]; } } } } // Conversion and compression for($i = 0, $l = count($tokens); $i < $l; $i++) { if ($tokens[$i][0] == self::T_DECLARATION) { // CSS Level 3 variables if ($sEmulateCcss3Variables) { if (substr($tokens[$i][2], 0, 4) == "var(" && substr($tokens[$i][2], -1, 1) == ")") { $tokens[$i][3][] = "all"; $variable = trim(substr($tokens[$i][2], 4, -1)); for($i2 = 0, $l2 = count($tokens[$i][3]); $i2 < $l2; $i2++) { if (isset($variables[$tokens[$i][3][$i2]][$variable])) { $tokens[$i][2] = $variables[$tokens[$i][3][$i2]][$variable]; break; } } } } // Compress unit values if ($sCompressUnitValues) { // Compress "0.5px" to ".5px" $tokens[$i][2] = preg_replace("/(^| |-)0\.([0-9]+)(%|em|ex|px|in|cm|mm|pt|pc)/iS", "\${1}.\${2}\${3}", $tokens[$i][2]); // Compress "0px" to "0" $tokens[$i][2] = preg_replace("/(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/iS", "\${1}0", $tokens[$i][2]); // Compress "0 0 0 0" to "0" if ($tokens[$i][2] == "0 0 0 0") {$tokens[$i][2] = "0";} } // Convert RGB color values to hex ("rgb(200,60%,5)" => "#c89905") if ($sConvertColorValues && preg_match("/rgb\s*\(\s*([0-9%]+)\s*,\s*([0-9%]+)\s*,\s*([0-9%]+)\s*\)/iS", $tokens[$i][2], $m)) { for ($i2 = 1, $l2 = count($m); $i2 < $l2; $i2++) { if (strpos("%", $m[$i2]) !== false) { $m[$i2] = substr($m[$i2], 0, -1); $m[$i2] = (int) (256 * ($m[$i2] / 100)); } $m[$i2] = str_pad(dechex($m[$i2]), 2, "0", STR_PAD_LEFT); } $tokens[$i][2] = str_replace($m[0], "#" . $m[1] . $m[2] . $m[3], $tokens[$i][2]); } // Compress color values ("#aabbcc" to "#abc") if ($sCompressColorValues && preg_match("/\#([0-9a-f]{6})/iS", $tokens[$i][2], $m)) { $m[1] = strtolower($m[1]); if (substr($m[1], 0, 1) == substr($m[1], 1, 1) && substr($m[1], 2, 1) == substr($m[1], 3, 1) && substr($m[1], 4, 1) == substr($m[1], 5, 1)) { $tokens[$i][2] = str_replace($m[0], "#" . substr($m[1], 0, 1) . substr($m[1], 2, 1) . substr($m[1], 4, 1), $tokens[$i][2]); } } } } // Create minified css $r = ""; for($i = 0, $l = count($tokens); $i < $l; $i++) { // T_AT_RULE if ($tokens[$i][0] == self::T_AT_RULE) { $r .= "@" . $tokens[$i][1] . " " . $tokens[$i][2] . ";"; } // T_AT_MEDIA_START elseif ($tokens[$i][0] == self::T_AT_MEDIA_START) { if (count($tokens[$i][1]) == 1 && $tokens[$i][1][0] == "all") { $r .= "@media{"; } else { $r .= "@media " . implode(",", $tokens[$i][1]) . "{"; } } // T_AT_FONT_FACE_START elseif ($tokens[$i][0] == self::T_AT_FONT_FACE_START) { $r .= "@font-face{"; } // T_FONT_FACE_DECLARATION elseif ($tokens[$i][0] == self::T_FONT_FACE_DECLARATION) { $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_AT_FONT_FACE_END ? "" : ";"); } // T_AT_PAGE_START elseif ($tokens[$i][0] == self::T_AT_PAGE_START) { $r .= "@page{"; } // T_PAGE_DECLARATION elseif ($tokens[$i][0] == self::T_PAGE_DECLARATION) { $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_AT_PAGE_END ? "" : ";"); } // T_SELECTORS elseif ($tokens[$i][0] == self::T_SELECTORS) { $r .= implode(",", $tokens[$i][1]); } // Start of declarations elseif ($tokens[$i][0] == self::T_DECLARATIONS_START) { $r .= "{"; } // T_DECLARATION elseif ($tokens[$i][0] == self::T_DECLARATION) { if ($sConvertCss3Properties && isset(self::$transformations[$tokens[$i][1]])) { foreach (self::$transformations[$tokens[$i][1]] as $value) { if (!is_array($value)) { $r .= $value . ":" . $tokens[$i][2] . ";"; } elseif (is_array($value) && is_callable($value)) { $r.= call_user_func_array($value, array($tokens[$i][1], $tokens[$i][2])); } } } $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_DECLARATIONS_END ? "" : ";"); } // T_DECLARATIONS_END, T_AT_MEDIA_END, T_AT_FONT_FACE_END, T_AT_PAGE_END elseif (in_array($tokens[$i][0], array(self::T_DECLARATIONS_END, self::T_AT_MEDIA_END, self::T_AT_FONT_FACE_END, self::T_AT_PAGE_END))) { $r .= "}"; } else { // Tokens with no output: // T_COMMENT // T_RULESET_START // T_RULESET_END // T_AT_VARIABLES_START // T_VARIABLE_DECLARATION // T_AT_VARIABLES_END } } return $r; } /** * Parses the Css and returns a array of tokens. * * @param string $css * @return array */ public static function parse($css) { // Settings $sDefaultScope = array("all"); // Default scope $sDefaultTrim = " \t\n\r\0\x0B"; // Default trim charlist $sTokenChars = "@{}();:\n\"'/*,"; // Tokens triggering parser processing // Basic variables $c = null; // Current char $p = null; // Previous char $buffer = ""; // Buffer $state = array(self::T_DOCUMENT); // State stack $currentState = self::T_DOCUMENT; // Current state $scope = $sDefaultScope; // Current scope $stringChar = null; // String delimiter char $isFilterWs = true; // Filter double whitespaces? $selectors = array(); // Array with collected selectors $r = array(); // Return value // Prepare css $css = str_replace("\r\n", "\n", $css); // Windows to Unix line endings $css = str_replace("\r", "\n", $css); // Mac to Unix line endings while (strpos($css, "\n\n") !== false) { $css = str_replace("\n\n", "\n", $css); // Remove double line endings } $css = str_replace("\t", " ", $css); // Convert tabs to spaces // Parse css for ($i = 0, $l = strlen($css); $i < $l; $i++) { $c = substr($css, $i, 1); // Filter out double spaces if ($isFilterWs && $c == " " && $c == $p) { continue; } $buffer .= $c; if (strpos($sTokenChars, $c) !== false) { // $currentState = $state[count($state) - 1]; /* * Start of comment */ if ($p == "/" && $c == "*" && $currentState != self::T_STRING && $currentState != self::T_COMMENT) { $saveBuffer = substr($buffer, 0, -2); // save the buffer (will get restored with comment ending) $buffer = $c; $isFilterWs = false; array_push($state, self::T_COMMENT); } /* * End of comment */ elseif ($p == "*" && $c == "/" && $currentState == self::T_COMMENT) { $r[] = array(self::T_COMMENT, trim($buffer)); $buffer = $saveBuffer; $isFilterWs = true; array_pop($state); } /* * Start of string */ elseif (($c == "\"" || $c == "'") && $currentState != self::T_STRING && $currentState != self::T_COMMENT && $currentState != self::T_STRING_URL) { $stringChar = $c; $isFilterWs = false; array_push($state, self::T_STRING); } /** * Escaped LF in string => remove escape backslash and LF */ elseif ($c == "\n" && $p == "\\" && $currentState == self::T_STRING) { $buffer = substr($buffer, 0, -2); } /* * End of string */ elseif ($c === $stringChar && $currentState == self::T_STRING) { if ($p == "\\") // Previous char is a escape char { $count = 1; $i2 = $i -2; while (substr($css, $i2, 1) == "\\") { $count++; $i2--; } // if count of escape chars is uneven => continue with string... if ($count % 2) { continue; } } // ...else end the string $isFilterWs = true; array_pop($state); $stringChar = null; } /** * Start of url string property */ elseif ($c == "(" && ($currentState != self::T_COMMENT && $currentState != self::T_STRING) && strtolower(substr($css, $i - 3, 3) == "url") && ($currentState == self::T_DECLARATION || $currentState == self::T_FONT_FACE_DECLARATION || $currentState == self::T_PAGE_DECLARATION || $currentState == self::T_VARIABLE_DECLARATION)) { array_push($state, self::T_STRING_URL); } /** * End of url string property */ elseif (($c == ")" || $c == "\n") && ($currentState != self::T_COMMENT && $currentState != self::T_STRING) && $currentState == self::T_STRING_URL) { if ($p == "\\") { continue; } array_pop($state); } /* * Start of at-rule @media block */ elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 6)) == "@media") { $i = $i + 6; $buffer = ""; array_push($state, self::T_AT_MEDIA_START); } /* * At-rule @media block media types */ elseif ($c == "{" && $currentState == self::T_AT_MEDIA_START) { $buffer = strtolower(trim($buffer, $sDefaultTrim . "{")); $scope = $buffer != "" ? array_filter(array_map("trim", explode(",", $buffer))) : $sDefaultScope; $r[] = array(self::T_AT_MEDIA_START, $scope); $i = $i++; $buffer = ""; array_pop($state); array_push($state, self::T_AT_MEDIA); } /* * End of at-rule @media block */ elseif ($currentState == self::T_AT_MEDIA && $c == "}") { $r[] = array(self::T_AT_MEDIA_END); $scope = $sDefaultScope; $buffer = ""; array_pop($state); } /* * Start of at-rule @font-face block */ elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 10)) == "@font-face") { $r[] = array(self::T_AT_FONT_FACE_START); $i = $i + 10; $buffer = ""; array_push($state, self::T_AT_FONT_FACE); } /* * @font-face declaration: Property */ elseif ($c == ":" && $currentState == self::T_AT_FONT_FACE) { $property = trim($buffer, $sDefaultTrim . ":{"); $buffer = ""; array_push($state, self::T_FONT_FACE_DECLARATION); } /* * @font-face declaration: Value */ elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_FONT_FACE_DECLARATION) { $value = trim($buffer, $sDefaultTrim . ";}"); $r[] = array(self::T_FONT_FACE_DECLARATION, $property, $value, $scope); $buffer = ""; array_pop($state); if ($c == "}") // @font-face declaration closed with a right curly brace => closes @font-face block { array_pop($state); $r[] = array(self::T_AT_FONT_FACE_END); } } /* * End of at-rule @font-face block */ elseif ($c == "}" && $currentState == self::T_AT_FONT_FACE) { $r[] = array(self::T_AT_FONT_FACE_END); $buffer = ""; array_pop($state); } /* * Start of at-rule @page block */ elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 5)) == "@page") { $r[] = array(self::T_AT_PAGE_START); $i = $i + 5; $buffer = ""; array_push($state, self::T_AT_PAGE); } /* * @page declaration: Property */ elseif ($c == ":" && $currentState == self::T_AT_PAGE) { $property = trim($buffer, $sDefaultTrim . ":{"); $buffer = ""; array_push($state, self::T_PAGE_DECLARATION); } /* * @page declaration: Value */ elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_PAGE_DECLARATION) { $value = trim($buffer, $sDefaultTrim . ";}"); $r[] = array(self::T_PAGE_DECLARATION, $property, $value, $scope); $buffer = ""; array_pop($state); if ($c == "}") // @page declaration closed with a right curly brace => closes @font-face block { array_pop($state); $r[] = array(self::T_AT_PAGE_END); } } /* * End of at-rule @page block */ elseif ($c == "}" && $currentState == self::T_AT_PAGE) { $r[] = array(self::T_AT_PAGE_END); $buffer = ""; array_pop($state); } /* * Start of at-rule @variables block */ elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 10)) == "@variables") { $i = $i + 10; $buffer = ""; array_push($state, self::T_AT_VARIABLES_START); } /* * @variables media types */ elseif ($c == "{" && $currentState == self::T_AT_VARIABLES_START) { $buffer = strtolower(trim($buffer, $sDefaultTrim . "{")); $r[] = array(self::T_AT_VARIABLES_START, $scope); $scope = $buffer != "" ? array_filter(array_map("trim", explode(",", $buffer))) : $sDefaultScope; $i = $i++; $buffer = ""; array_pop($state); array_push($state, self::T_AT_VARIABLES); } /* * @variables declaration: Property */ elseif ($c == ":" && $currentState == self::T_AT_VARIABLES) { $property = trim($buffer, $sDefaultTrim . ":"); $buffer = ""; array_push($state, self::T_VARIABLE_DECLARATION); } /* * @variables declaration: Value */ elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_VARIABLE_DECLARATION) { $value = trim($buffer, $sDefaultTrim . ";}"); $r[] = array(self::T_VARIABLE_DECLARATION, $property, $value, $scope); $buffer = ""; array_pop($state); if ($c == "}") // @variable declaration closed with a right curly brace => closes @variables block { array_pop($state); $r[] = array(self::T_AT_VARIABLES_END); $scope = $sDefaultScope; } } /* * End of at-rule @variables block */ elseif ($c == "}" && $currentState == self::T_AT_VARIABLES) { $r[] = array(self::T_AT_VARIABLES_END); $scope = $sDefaultScope; $buffer = ""; array_pop($state); } /* * Start of document level at-rule */ elseif ($c == "@" && $currentState == self::T_DOCUMENT) { $buffer = ""; array_push($state, self::T_AT_RULE); } /* * End of document level at-rule */ elseif ($c == ";" && $currentState == self::T_AT_RULE) { $pos = strpos($buffer, " "); $rule = substr($buffer, 0, $pos); $value = trim(substr($buffer, $pos), $sDefaultTrim . ";"); $r[] = array(self::T_AT_RULE, $rule, $value); $buffer = ""; array_pop($state); } /** * Selector */ elseif ($c == "," && ($currentState == self::T_AT_MEDIA || $currentState == self::T_DOCUMENT)) { $selectors[]= trim($buffer, $sDefaultTrim . ","); $buffer = ""; } /* * Start of ruleset */ elseif ($c == "{" && ($currentState == self::T_AT_MEDIA || $currentState == self::T_DOCUMENT)) { $selectors[]= trim($buffer, $sDefaultTrim . "{"); $selectors = array_filter(array_map("trim", $selectors)); $r[] = array(self::T_RULESET_START); $r[] = array(self::T_SELECTORS, $selectors); $r[] = array(self::T_DECLARATIONS_START); $buffer = ""; $selectors = array(); array_push($state, self::T_DECLARATIONS); } /* * Declaration: Property */ elseif ($c == ":" && $currentState == self::T_DECLARATIONS) { $property = trim($buffer, $sDefaultTrim . ":;"); $buffer = ""; array_push($state, self::T_DECLARATION); } /* * Declaration: Value */ elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_DECLARATION) { $value = trim($buffer, $sDefaultTrim . ";}"); $r[] = array(self::T_DECLARATION, $property, $value, $scope); $buffer = ""; array_pop($state); if ($c == "}") // declaration closed with a right curly brace => close ruleset { array_pop($state); $r[] = array(self::T_DECLARATIONS_END); $r[] = array(self::T_RULESET_END); } } /* * End of ruleset */ elseif ($c == "}" && $currentState == self::T_DECLARATIONS) { $r[] = array(self::T_DECLARATIONS_END); $r[] = array(self::T_RULESET_END); $buffer = ""; array_pop($state); } } $p = $c; } return $r; } /** * Transforms "opacity: {value}" into browser specific counterparts. * * @param string $property * @param string $value * @return string */ private static function _tOpacity($property, $value) { $ieValue = (int) ((float) $value * 100); $r = "-moz-opacity:" . $value . ";"; // Firefox < 3.5 $r .= "-ms-filter: \"alpha(opacity=" . $ieValue . ")\";"; // Internet Explorer 8 $r .= "filter: alpha(opacity=" . $ieValue . ");zoom: 1;"; // Internet Explorer 4 - 7 return $r; } /** * Transforms "white-space: pre-wrap" into browser specific counterparts. * * @param string $property * @param string $value * @return string */ private static function _tWhiteSpacePreWrap($property, $value) { if (strtolower($value) == "pre-wrap") { $r = "white-space:-moz-pre-wrap;"; // Mozilla $r .= "white-space:-webkit-pre-wrap;"; // Webkit $r .= "white-space:-khtml-pre-wrap;"; // khtml $r .= "white-space:-pre-wrap;"; // Opera 4 - 6 $r .= "white-space:-o-pre-wrap;"; // Opera 7+ $r .= "word-wrap:break-word;"; // Internet Explorer 5.5+ return $r; } else { return ""; } } } ?>