|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00021 class Minify_CSS_Compressor { 00022 00032 public static function process($css, $options = array()) 00033 { 00034 $obj = new Minify_CSS_Compressor($options); 00035 return $obj->_process($css); 00036 } 00037 00041 protected $_options = null; 00042 00048 protected $_inHack = false; 00049 00050 00058 private function __construct($options) { 00059 $this->_options = $options; 00060 } 00061 00069 protected function _process($css) 00070 { 00071 $css = str_replace("\r\n", "\n", $css); 00072 00073 // preserve empty comment after '>' 00074 // http://www.webdevout.net/css-hacks#in_css-selectors 00075 $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css); 00076 00077 // preserve empty comment between property and value 00078 // http://css-discuss.incutio.com/?page=BoxModelHack 00079 $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css); 00080 $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); 00081 00082 // apply callback to all valid comments (and strip out surrounding ws 00083 $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' 00084 ,array($this, '_commentCB'), $css); 00085 00086 // remove ws around { } and last semicolon in declaration block 00087 $css = preg_replace('/\\s*{\\s*/', '{', $css); 00088 $css = preg_replace('/;?\\s*}\\s*/', '}', $css); 00089 00090 // remove ws surrounding semicolons 00091 $css = preg_replace('/\\s*;\\s*/', ';', $css); 00092 00093 // remove ws around urls 00094 $css = preg_replace('/ 00095 url\\( # url( 00096 \\s* 00097 ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) 00098 \\s* 00099 \\) # ) 00100 /x', 'url($1)', $css); 00101 00102 // remove ws between rules and colons 00103 $css = preg_replace('/ 00104 \\s* 00105 ([{;]) # 1 = beginning of block or rule separator 00106 \\s* 00107 ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter) 00108 \\s* 00109 : 00110 \\s* 00111 (\\b|[#\'"]) # 3 = first character of a value 00112 /x', '$1$2:$3', $css); 00113 00114 // remove ws in selectors 00115 $css = preg_replace_callback('/ 00116 (?: # non-capture 00117 \\s* 00118 [^~>+,\\s]+ # selector part 00119 \\s* 00120 [,>+~] # combinators 00121 )+ 00122 \\s* 00123 [^~>+,\\s]+ # selector part 00124 { # open declaration block 00125 /x' 00126 ,array($this, '_selectorsCB'), $css); 00127 00128 // minimize hex colors 00129 $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' 00130 , '$1#$2$3$4$5', $css); 00131 00132 // remove spaces between font families 00133 $css = preg_replace_callback('/font-family:([^;}]+)([;}])/' 00134 ,array($this, '_fontFamilyCB'), $css); 00135 00136 $css = preg_replace('/@import\\s+url/', '@import url', $css); 00137 00138 // replace any ws involving newlines with a single newline 00139 $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); 00140 00141 // separate common descendent selectors w/ newlines (to limit line lengths) 00142 $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); 00143 00144 // Use newline after 1st numeric value (to limit line lengths). 00145 $css = preg_replace('/ 00146 ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value 00147 \\s+ 00148 /x' 00149 ,"$1\n", $css); 00150 00151 // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ 00152 $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); 00153 00154 return trim($css); 00155 } 00156 00164 protected function _selectorsCB($m) 00165 { 00166 // remove ws around the combinators 00167 return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]); 00168 } 00169 00177 protected function _commentCB($m) 00178 { 00179 $hasSurroundingWs = (trim($m[0]) !== $m[1]); 00180 $m = $m[1]; 00181 // $m is the comment content w/o the surrounding tokens, 00182 // but the return value will replace the entire comment. 00183 if ($m === 'keep') { 00184 return '/**/'; 00185 } 00186 if ($m === '" "') { 00187 // component of http://tantek.com/CSS/Examples/midpass.html 00188 return '/*" "*/'; 00189 } 00190 if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { 00191 // component of http://tantek.com/CSS/Examples/midpass.html 00192 return '/*";}}/* */'; 00193 } 00194 if ($this->_inHack) { 00195 // inversion: feeding only to one browser 00196 if (preg_match('@ 00197 ^/ # comment started like /*/ 00198 \\s* 00199 (\\S[\\s\\S]+?) # has at least some non-ws content 00200 \\s* 00201 /\\* # ends like /*/ or /**/ 00202 @x', $m, $n)) { 00203 // end hack mode after this comment, but preserve the hack and comment content 00204 $this->_inHack = false; 00205 return "/*/{$n[1]}/**/"; 00206 } 00207 } 00208 if (substr($m, -1) === '\\') { // comment ends like \*/ 00209 // begin hack mode and preserve hack 00210 $this->_inHack = true; 00211 return '/*\\*/'; 00212 } 00213 if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ 00214 // begin hack mode and preserve hack 00215 $this->_inHack = true; 00216 return '/*/*/'; 00217 } 00218 if ($this->_inHack) { 00219 // a regular comment ends hack mode but should be preserved 00220 $this->_inHack = false; 00221 return '/**/'; 00222 } 00223 // Issue 107: if there's any surrounding whitespace, it may be important, so 00224 // replace the comment with a single space 00225 return $hasSurroundingWs // remove all other comments 00226 ? ' ' 00227 : ''; 00228 } 00229 00237 protected function _fontFamilyCB($m) 00238 { 00239 // Issue 210: must not eliminate WS between words in unquoted families 00240 $pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 00241 $out = 'font-family:'; 00242 while (null !== ($piece = array_shift($pieces))) { 00243 if ($piece[0] !== '"' && $piece[0] !== "'") { 00244 $piece = preg_replace('/\\s+/', ' ', $piece); 00245 $piece = preg_replace('/\\s?,\\s?/', ',', $piece); 00246 } 00247 $out .= $piece; 00248 } 00249 return $out . $m[2]; 00250 } 00251 }