]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Minify/extlib/minify/min/lib/Minify/CSS/Compressor.php
Added minify plugin
[quix0rs-gnu-social.git] / plugins / Minify / extlib / minify / min / lib / Minify / CSS / Compressor.php
1 <?php\r
2 /**\r
3  * Class Minify_CSS_Compressor \r
4  * @package Minify\r
5  */\r
6 \r
7 /**\r
8  * Compress CSS\r
9  *\r
10  * This is a heavy regex-based removal of whitespace, unnecessary\r
11  * comments and tokens, and some CSS value minimization, where practical.\r
12  * Many steps have been taken to avoid breaking comment-based hacks, \r
13  * including the ie5/mac filter (and its inversion), but expect tricky\r
14  * hacks involving comment tokens in 'content' value strings to break\r
15  * minimization badly. A test suite is available.\r
16  * \r
17  * @package Minify\r
18  * @author Stephen Clay <steve@mrclay.org>\r
19  * @author http://code.google.com/u/1stvamp/ (Issue 64 patch)\r
20  */\r
21 class Minify_CSS_Compressor {\r
22 \r
23     /**\r
24      * Minify a CSS string\r
25      * \r
26      * @param string $css\r
27      * \r
28      * @param array $options (currently ignored)\r
29      * \r
30      * @return string\r
31      */\r
32     public static function process($css, $options = array())\r
33     {\r
34         $obj = new Minify_CSS_Compressor($options);\r
35         return $obj->_process($css);\r
36     }\r
37     \r
38     /**\r
39      * @var array options\r
40      */\r
41     protected $_options = null;\r
42     \r
43     /**\r
44      * @var bool Are we "in" a hack?\r
45      * \r
46      * I.e. are some browsers targetted until the next comment?\r
47      */\r
48     protected $_inHack = false;\r
49     \r
50     \r
51     /**\r
52      * Constructor\r
53      * \r
54      * @param array $options (currently ignored)\r
55      * \r
56      * @return null\r
57      */\r
58     private function __construct($options) {\r
59         $this->_options = $options;\r
60     }\r
61     \r
62     /**\r
63      * Minify a CSS string\r
64      * \r
65      * @param string $css\r
66      * \r
67      * @return string\r
68      */\r
69     protected function _process($css)\r
70     {\r
71         $css = str_replace("\r\n", "\n", $css);\r
72         \r
73         // preserve empty comment after '>'\r
74         // http://www.webdevout.net/css-hacks#in_css-selectors\r
75         $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);\r
76         \r
77         // preserve empty comment between property and value\r
78         // http://css-discuss.incutio.com/?page=BoxModelHack\r
79         $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);\r
80         $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);\r
81         \r
82         // apply callback to all valid comments (and strip out surrounding ws\r
83         $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'\r
84             ,array($this, '_commentCB'), $css);\r
85 \r
86         // remove ws around { } and last semicolon in declaration block\r
87         $css = preg_replace('/\\s*{\\s*/', '{', $css);\r
88         $css = preg_replace('/;?\\s*}\\s*/', '}', $css);\r
89         \r
90         // remove ws surrounding semicolons\r
91         $css = preg_replace('/\\s*;\\s*/', ';', $css);\r
92         \r
93         // remove ws around urls\r
94         $css = preg_replace('/\r
95                 url\\(      # url(\r
96                 \\s*\r
97                 ([^\\)]+?)  # 1 = the URL (really just a bunch of non right parenthesis)\r
98                 \\s*\r
99                 \\)         # )\r
100             /x', 'url($1)', $css);\r
101         \r
102         // remove ws between rules and colons\r
103         $css = preg_replace('/\r
104                 \\s*\r
105                 ([{;])              # 1 = beginning of block or rule separator \r
106                 \\s*\r
107                 ([\\*_]?[\\w\\-]+)  # 2 = property (and maybe IE filter)\r
108                 \\s*\r
109                 :\r
110                 \\s*\r
111                 (\\b|[#\'"])        # 3 = first character of a value\r
112             /x', '$1$2:$3', $css);\r
113         \r
114         // remove ws in selectors\r
115         $css = preg_replace_callback('/\r
116                 (?:              # non-capture\r
117                     \\s*\r
118                     [^~>+,\\s]+  # selector part\r
119                     \\s*\r
120                     [,>+~]       # combinators\r
121                 )+\r
122                 \\s*\r
123                 [^~>+,\\s]+      # selector part\r
124                 {                # open declaration block\r
125             /x'\r
126             ,array($this, '_selectorsCB'), $css);\r
127         \r
128         // minimize hex colors\r
129         $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'\r
130             , '$1#$2$3$4$5', $css);\r
131         \r
132         // remove spaces between font families\r
133         $css = preg_replace_callback('/font-family:([^;}]+)([;}])/'\r
134             ,array($this, '_fontFamilyCB'), $css);\r
135         \r
136         $css = preg_replace('/@import\\s+url/', '@import url', $css);\r
137         \r
138         // replace any ws involving newlines with a single newline\r
139         $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);\r
140         \r
141         // separate common descendent selectors w/ newlines (to limit line lengths)\r
142         $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);\r
143         \r
144         // Use newline after 1st numeric value (to limit line lengths).\r
145         $css = preg_replace('/\r
146             ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value\r
147             \\s+\r
148             /x'\r
149             ,"$1\n", $css);\r
150         \r
151         // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/\r
152         $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);\r
153             \r
154         return trim($css);\r
155     }\r
156     \r
157     /**\r
158      * Replace what looks like a set of selectors  \r
159      *\r
160      * @param array $m regex matches\r
161      * \r
162      * @return string\r
163      */\r
164     protected function _selectorsCB($m)\r
165     {\r
166         // remove ws around the combinators\r
167         return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);\r
168     }\r
169     \r
170     /**\r
171      * Process a comment and return a replacement\r
172      * \r
173      * @param array $m regex matches\r
174      * \r
175      * @return string\r
176      */\r
177     protected function _commentCB($m)\r
178     {\r
179         $hasSurroundingWs = (trim($m[0]) !== $m[1]);\r
180         $m = $m[1]; \r
181         // $m is the comment content w/o the surrounding tokens, \r
182         // but the return value will replace the entire comment.\r
183         if ($m === 'keep') {\r
184             return '/**/';\r
185         }\r
186         if ($m === '" "') {\r
187             // component of http://tantek.com/CSS/Examples/midpass.html\r
188             return '/*" "*/';\r
189         }\r
190         if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {\r
191             // component of http://tantek.com/CSS/Examples/midpass.html\r
192             return '/*";}}/* */';\r
193         }\r
194         if ($this->_inHack) {\r
195             // inversion: feeding only to one browser\r
196             if (preg_match('@\r
197                     ^/               # comment started like /*/\r
198                     \\s*\r
199                     (\\S[\\s\\S]+?)  # has at least some non-ws content\r
200                     \\s*\r
201                     /\\*             # ends like /*/ or /**/\r
202                 @x', $m, $n)) {\r
203                 // end hack mode after this comment, but preserve the hack and comment content\r
204                 $this->_inHack = false;\r
205                 return "/*/{$n[1]}/**/";\r
206             }\r
207         }\r
208         if (substr($m, -1) === '\\') { // comment ends like \*/\r
209             // begin hack mode and preserve hack\r
210             $this->_inHack = true;\r
211             return '/*\\*/';\r
212         }\r
213         if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */\r
214             // begin hack mode and preserve hack\r
215             $this->_inHack = true;\r
216             return '/*/*/';\r
217         }\r
218         if ($this->_inHack) {\r
219             // a regular comment ends hack mode but should be preserved\r
220             $this->_inHack = false;\r
221             return '/**/';\r
222         }\r
223         // Issue 107: if there's any surrounding whitespace, it may be important, so \r
224         // replace the comment with a single space\r
225         return $hasSurroundingWs // remove all other comments\r
226             ? ' '\r
227             : '';\r
228     }\r
229     \r
230     /**\r
231      * Process a font-family listing and return a replacement\r
232      * \r
233      * @param array $m regex matches\r
234      * \r
235      * @return string   \r
236      */\r
237     protected function _fontFamilyCB($m)\r
238     {\r
239         $m[1] = preg_replace('/\r
240                 \\s*\r
241                 (\r
242                     "[^"]+"      # 1 = family in double qutoes\r
243                     |\'[^\']+\'  # or 1 = family in single quotes\r
244                     |[\\w\\-]+   # or 1 = unquoted family\r
245                 )\r
246                 \\s*\r
247             /x', '$1', $m[1]);\r
248         return 'font-family:' . $m[1] . $m[2];\r
249     }\r
250 }\r