initial commit
[namibia] / public / min / lib / Minify / YUI / CssCompressor.php
1 <?php
2 /**
3  * Class Minify_YUI_CssCompressor 
4  * @package Minify
5  *
6  * YUI Compressor
7  * Author: Julien Lecomte -  http://www.julienlecomte.net/
8  * Author: Isaac Schlueter - http://foohack.com/
9  * Author: Stoyan Stefanov - http://phpied.com/
10  * Author: Steve Clay      - http://www.mrclay.org/ (PHP port)
11  * Copyright (c) 2009 Yahoo! Inc.  All rights reserved.
12  * The copyrights embodied in the content of this file are licensed
13  * by Yahoo! Inc. under the BSD (revised) open source license.
14  */
15
16 /**
17  * Compress CSS (incomplete DO NOT USE)
18  * 
19  * @see https://github.com/yui/yuicompressor/blob/master/src/com/yahoo/platform/yui/compressor/CssCompressor.java
20  *
21  * @package Minify
22  */
23 class Minify_YUI_CssCompressor {
24
25     /**
26      * Minify a CSS string
27      *
28      * @param string $css
29      *
30      * @return string
31      */
32     public function compress($css, $linebreakpos = 0)
33     {
34         $css = str_replace("\r\n", "\n", $css);
35
36         /**
37          * @todo comment removal
38          * @todo re-port from newer Java version
39          */
40
41         // Normalize all whitespace strings to single spaces. Easier to work with that way.
42         $css = preg_replace('@\s+@', ' ', $css);
43
44         // Make a pseudo class for the Box Model Hack
45         $css = preg_replace("@\"\\\\\"}\\\\\"\"@", "___PSEUDOCLASSBMH___", $css);
46
47         // Remove the spaces before the things that should not have spaces before them.
48         // But, be careful not to turn "p :link {...}" into "p:link{...}"
49         // Swap out any pseudo-class colons with the token, and then swap back.
50         $css = preg_replace_callback("@(^|\\})(([^\\{:])+:)+([^\\{]*\\{)@", array($this, '_removeSpacesCB'), $css);
51
52         $css = preg_replace("@\\s+([!{};:>+\\(\\)\\],])@", "$1", $css);
53         $css = str_replace("___PSEUDOCLASSCOLON___", ":", $css);
54
55         // Remove the spaces after the things that should not have spaces after them.
56         $css = preg_replace("@([!{}:;>+\\(\\[,])\\s+@", "$1", $css);
57
58         // Add the semicolon where it's missing.
59         $css = preg_replace("@([^;\\}])}@", "$1;}", $css);
60
61         // Replace 0(px,em,%) with 0.
62         $css = preg_replace("@([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)@", "$1$2", $css);
63
64         // Replace 0 0 0 0; with 0.
65         $css = str_replace(":0 0 0 0;", ":0;", $css);
66         $css = str_replace(":0 0 0;", ":0;", $css);
67         $css = str_replace(":0 0;", ":0;", $css);
68
69         // Replace background-position:0; with background-position:0 0;
70         $css = str_replace("background-position:0;", "background-position:0 0;", $css);
71
72         // Replace 0.6 to .6, but only when preceded by : or a white-space
73         $css = preg_replace("@(:|\\s)0+\\.(\\d+)@", "$1.$2", $css);
74
75         // Shorten colors from rgb(51,102,153) to #336699
76         // This makes it more likely that it'll get further compressed in the next step.
77         $css = preg_replace_callback("@rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)@", array($this, '_shortenRgbCB'), $css);
78
79         // Shorten colors from #AABBCC to #ABC. Note that we want to make sure
80         // the color is not preceded by either ", " or =. Indeed, the property
81         //     filter: chroma(color="#FFFFFF");
82         // would become
83         //     filter: chroma(color="#FFF");
84         // which makes the filter break in IE.
85         $css = preg_replace_callback("@([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])@", array($this, '_shortenHexCB'), $css);
86
87         // Remove empty rules.
88         $css = preg_replace("@[^\\}]+\\{;\\}@", "", $css);
89
90         $linebreakpos = isset($this->_options['linebreakpos'])
91             ? $this->_options['linebreakpos']
92             : 0;
93
94         if ($linebreakpos > 0) {
95             // Some source control tools don't like it when files containing lines longer
96             // than, say 8000 characters, are checked in. The linebreak option is used in
97             // that case to split long lines after a specific column.
98             $i = 0;
99             $linestartpos = 0;
100             $sb = $css;
101
102             // make sure strlen returns byte count
103             $mbIntEnc = null;
104             if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
105                 $mbIntEnc = mb_internal_encoding();
106                 mb_internal_encoding('8bit');
107             }
108             $sbLength = strlen($css);
109             while ($i < $sbLength) {
110                 $c = $sb[$i++];
111                 if ($c === '}' && $i - $linestartpos > $linebreakpos) {
112                     $sb = substr_replace($sb, "\n", $i, 0);
113                     $sbLength++;
114                     $linestartpos = $i;
115                 }
116             }
117             $css = $sb;
118
119             // undo potential mb_encoding change
120             if ($mbIntEnc !== null) {
121                 mb_internal_encoding($mbIntEnc);
122             }
123         }
124
125         // Replace the pseudo class for the Box Model Hack
126         $css = str_replace("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\"", $css);
127
128         // Replace multiple semi-colons in a row by a single one
129         // See SF bug #1980989
130         $css = preg_replace("@;;+@", ";", $css);
131
132         // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
133         $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
134
135         // Trim the final string (for any leading or trailing white spaces)
136         $css = trim($css);
137
138         return $css;
139     }
140
141     protected function _removeSpacesCB($m)
142     {
143         return str_replace(':', '___PSEUDOCLASSCOLON___', $m[0]);
144     }
145
146     protected function _shortenRgbCB($m)
147     {
148         $rgbcolors = explode(',', $m[1]);
149         $hexcolor = '#';
150         for ($i = 0; $i < count($rgbcolors); $i++) {
151             $val = round($rgbcolors[$i]);
152             if ($val < 16) {
153                 $hexcolor .= '0';
154             }
155             $hexcolor .= dechex($val);
156         }
157         return $hexcolor;
158     }
159
160     protected function _shortenHexCB($m)
161     {
162         // Test for AABBCC pattern
163         if ((strtolower($m[3])===strtolower($m[4])) &&
164                 (strtolower($m[5])===strtolower($m[6])) &&
165                 (strtolower($m[7])===strtolower($m[8]))) {
166             return $m[1] . $m[2] . "#" . $m[3] . $m[5] . $m[7];
167         } else {
168             return $m[0];
169         }
170     }
171 }