source: trunk/include/cssmin.class.php @ 10965

Last change on this file since 10965 was 8170, checked in by rvelices, 14 years ago

combined css and js are minified

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Author Date Id Revision
File size: 24.6 KB
Line 
1<?php
2/**
3 * CssMin - A (simple) css minifier with benefits
4 *
5 * --
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
8 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
9 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
10 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
11 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12 * --
13 *
14 * @package             CssMin
15 * @author              Joe Scylla <joe.scylla@gmail.com>
16 * @copyright   2008 - 2010 Joe Scylla <joe.scylla@gmail.com>
17 * @license             http://opensource.org/licenses/mit-license.php MIT License
18 * @version             2.0.1.0064 (2010-09-30)
19 */
20class CssMin
21        {
22        /**
23         * State: Is in document
24         *
25         * @var integer
26         */
27        const T_DOCUMENT = 1;
28        /**
29         * Token: Comment
30         *
31         * @var integer
32         */
33        const T_COMMENT = 2;
34        /**
35         * Token: Generic at-rule
36         *
37         * @var integer
38         */
39        const T_AT_RULE = 3;
40        /**
41         * Token: Start of @media block
42         *
43         * @var integer
44         */
45        const T_AT_MEDIA_START = 4;
46        /**
47         * State: Is in @media block
48         *
49         * @var integer
50         */
51        const T_AT_MEDIA = 5;
52        /**
53         * Token: End of @media block
54         *
55         * @var integer
56         */
57        const T_AT_MEDIA_END = 6;
58        /**
59         * Token: Start of @font-face block
60         *
61         * @var integer
62         */
63        const T_AT_FONT_FACE_START = 7;
64        /**
65         * State: Is in @font-face block
66         *
67         * @var integer
68         */
69        const T_AT_FONT_FACE = 8;
70        /**
71         * Token: @font-face declaration
72         *
73         * @var integer
74         */
75        const T_FONT_FACE_DECLARATION = 9;
76        /**
77         * Token: End of @font-face block
78         *
79         * @var integer
80         */
81        const T_AT_FONT_FACE_END = 10;
82        /**
83         * Token: Start of @page block
84         *
85         * @var integer
86         */
87        const T_AT_PAGE_START = 11;
88        /**
89         * State: Is in @page block
90         *
91         * @var integer
92         */
93        const T_AT_PAGE = 12;
94        /**
95         * Token: @page declaration
96         *
97         * @var integer
98         */
99        const T_PAGE_DECLARATION = 13;
100        /**
101         * Token: End of @page block
102         *
103         * @var integer
104         */
105        const T_AT_PAGE_END = 14;
106        /**
107         * Token: Start of ruleset
108         *
109         * @var integer
110         */
111        const T_RULESET_START = 15;
112        /**
113         * Token: Ruleset selectors
114         *
115         * @var integer
116         */
117        const T_SELECTORS = 16;
118        /**
119         * Token: Start of declarations
120         *
121         * @var integer
122         */
123        const T_DECLARATIONS_START = 17;
124        /**
125         * State: Is in declarations
126         *
127         * @var integer
128         */
129        const T_DECLARATIONS = 18;
130        /**
131         * Token: Declaration
132         *
133         * @var integer
134         */
135        const T_DECLARATION = 19;
136        /**
137         * Token: End of declarations
138         *
139         * @var integer
140         */
141        const T_DECLARATIONS_END = 20;
142        /**
143         * Token: End of ruleset
144         *
145         * @var integer
146         */
147        const T_RULESET_END = 21;
148        /**
149         * Token: Start of @variables block
150         *
151         * @var integer
152         */
153        const T_AT_VARIABLES_START = 100;
154        /**
155         * State: Is in @variables block
156         *
157         * @var integer
158         */
159        const T_AT_VARIABLES = 101;
160        /**
161         * Token: @variables declaration
162         *
163         * @var integer
164         */
165        const T_VARIABLE_DECLARATION = 102;
166        /**
167         * Token: End of @variables block
168         *
169         * @var integer
170         */
171        const T_AT_VARIABLES_END = 103;
172        /**
173         * State: Is in string
174         *
175         * @var integer
176         */
177        const T_STRING = 254;
178        /**
179         * State: Is in url string property
180         *
181         * @var integer
182         */
183        const T_STRING_URL = 255;
184        /**
185         * Css transformations table
186         *
187         * @var array
188         */
189        private static $transformations = array
190                (
191                "border-radius"                                 => array("-moz-border-radius", "-webkit-border-radius", "-khtml-border-radius"),
192                "border-top-left-radius"                => array("-moz-border-radius-topleft", "-webkit-border-top-left-radius", "-khtml-top-left-radius"),
193                "border-top-right-radius"               => array("-moz-border-radius-topright", "-webkit-border-top-right-radius", "-khtml-top-right-radius"),
194                "border-bottom-right-radius"    => array("-moz-border-radius-bottomright", "-webkit-border-bottom-right-radius", "-khtml-border-bottom-right-radius"),
195                "border-bottom-left-radius"             => array("-moz-border-radius-bottomleft", "-webkit-border-bottom-left-radius", "-khtml-border-bottom-left-radius"),
196                "box-shadow"                                    => array("-moz-box-shadow", "-webkit-box-shadow", "-khtml-box-shadow"),
197                "opacity"                                               => array(array("CssMin", "_tOpacity")),
198                "text-shadow"                                   => array("-moz-text-shadow", "-webkit-text-shadow", "-khtml-text-shadow"),
199                "white-space"                                   => array(array("CssMin", "_tWhiteSpacePreWrap"))
200                );
201        /**
202         * Minifies the Css.
203         *
204         * @param string $css
205         * @param array $config [optional]
206         * @return string
207         */
208        public static function minify($css, $config = array())
209                {
210                $tokens = self::parse($css);
211                $config = array_merge(array
212                        (
213                        "remove-empty-blocks"                   => true,
214                        "remove-empty-rulesets"                 => true,
215                        "remove-last-semicolons"                => true,
216                        "convert-css3-properties"               => false,
217                        "convert-color-values"                  => false,
218                        "compress-color-values"                 => false,
219                        "compress-unit-values"                  => false,
220                        "emulate-css3-variables"                => true,
221                        ), $config);
222                // Minification options
223                $sRemoveEmptyBlocks                     = $config["remove-empty-blocks"]; 
224                $sRemoveEmptyRulesets           = $config["remove-empty-rulesets"];
225                $sRemoveLastSemicolon           = $config["remove-last-semicolons"];
226                $sConvertCss3Properties         = $config["convert-css3-properties"];
227                $sCompressUnitValues            = $config["compress-unit-values"];
228                $sConvertColorValues            = $config["convert-color-values"];
229                $sCompressColorValues           = $config["compress-color-values"];
230                $sEmulateCcss3Variables         = $config["emulate-css3-variables"];
231                $sRemoveTokens                          = array(self::T_COMMENT);
232                // Remove tokens
233                if (!$sEmulateCcss3Variables)
234                        {
235                        $sRemoveTokens = array_merge($sRemoveTokens, array(self::T_AT_VARIABLES_START, self::T_VARIABLE_DECLARATION, self::T_AT_VARIABLES_END));
236                        }
237                for($i = 0, $l = count($tokens); $i < $l; $i++)
238                        {
239                        if (in_array($tokens[$i][0], $sRemoveTokens))
240                                {
241                                unset($tokens[$i]);
242                                }
243                        }
244                $tokens = array_values($tokens);
245                // Remove empty rulesets
246                if ($sRemoveEmptyRulesets)
247                        {
248                        for($i = 0, $l = count($tokens); $i < $l; $i++)
249                                {
250                                // Remove empty rulesets
251                                if ($tokens[$i][0] == self::T_RULESET_START && $tokens[$i+4][0] == self::T_RULESET_END)
252                                        {
253                                        unset($tokens[$i]);     // T_RULESET_START
254                                        unset($tokens[++$i]);   // T_SELECTORS
255                                        unset($tokens[++$i]);   // T_DECLARATIONS_START
256                                        unset($tokens[++$i]);   // T_DECLARATIONS_END
257                                        unset($tokens[++$i]);   // T_RULESET_END
258                                        }
259                                }
260                        $tokens = array_values($tokens);
261                        }
262                // Remove empty @media, @font-face or @page blocks
263                if ($sRemoveEmptyBlocks)
264                        {
265                        for($i = 0, $l = count($tokens); $i < $l; $i++)
266                                {
267                                // Remove empty @media, @font-face or @page blocks
268                                if (($tokens[$i][0] == self::T_AT_MEDIA_START && $tokens[$i+1][0] == self::T_AT_MEDIA_END)
269                                        || ($tokens[$i][0] == self::T_AT_FONT_FACE_START && $tokens[$i+1][0] == self::T_AT_FONT_FACE_END)
270                                        || ($tokens[$i][0] == self::T_AT_PAGE_START && $tokens[$i+1][0] == self::T_AT_PAGE_END))
271                                        {
272                                        unset($tokens[$i]);             // T_AT_MEDIA_START, T_AT_FONT_FACE_START, T_AT_PAGE_START
273                                        unset($tokens[++$i]);   // T_AT_MEDIA_END, T_AT_FONT_FACE_END, T_AT_PAGE_END
274                                        }
275                                }
276                        $tokens = array_values($tokens);
277                        }
278                // CSS Level 3 variables: parse variables
279                if ($sEmulateCcss3Variables)
280                        {
281                        // Parse variables
282                        $variables = array();
283                        for($i = 0, $l = count($tokens); $i < $l; $i++)
284                                {
285                                if ($tokens[$i][0] == self::T_VARIABLE_DECLARATION)
286                                        {
287                                        for($i2 = 0, $l2 = count($tokens[$i][3]); $i2 < $l2; $i2++)
288                                                {
289                                                if (!isset($variables[$tokens[$i][3][$i2]]))
290                                                        {
291                                                        $variables[$tokens[$i][3][$i2]] = array();
292                                                        }
293                                                $variables[$tokens[$i][3][$i2]][$tokens[$i][1]] = $tokens[$i][2];
294                                                }
295                                        }
296                                }
297                        }
298                // Conversion and compression
299                for($i = 0, $l = count($tokens); $i < $l; $i++)
300                        {
301                        if ($tokens[$i][0] == self::T_DECLARATION)
302                                {
303                                // CSS Level 3 variables
304                                if ($sEmulateCcss3Variables)
305                                        {
306                                        if (substr($tokens[$i][2], 0, 4) == "var(" && substr($tokens[$i][2], -1, 1) == ")")
307                                                {
308                                                $tokens[$i][3][] = "all";
309                                                $variable = trim(substr($tokens[$i][2], 4, -1));
310                                                for($i2 = 0, $l2 = count($tokens[$i][3]); $i2 < $l2; $i2++)
311                                                        {
312                                                        if (isset($variables[$tokens[$i][3][$i2]][$variable]))
313                                                                {
314                                                                $tokens[$i][2] = $variables[$tokens[$i][3][$i2]][$variable];
315                                                                break;
316                                                                }
317                                                        }
318                                                }
319                                        }
320                                // Compress unit values
321                                if ($sCompressUnitValues)
322                                        {
323                                        // Compress "0.5px" to ".5px"
324                                        $tokens[$i][2] = preg_replace("/(^| |-)0\.([0-9]+)(%|em|ex|px|in|cm|mm|pt|pc)/iS", "\${1}.\${2}\${3}", $tokens[$i][2]);
325                                        // Compress "0px" to "0"
326                                        $tokens[$i][2] = preg_replace("/(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/iS", "\${1}0", $tokens[$i][2]);
327                                        // Compress "0 0 0 0" to "0"
328                                        if ($tokens[$i][2] == "0 0 0 0") {$tokens[$i][2] = "0";}
329                                        }
330                                // Convert RGB color values to hex ("rgb(200,60%,5)" => "#c89905")
331                                if ($sConvertColorValues && preg_match("/rgb\s*\(\s*([0-9%]+)\s*,\s*([0-9%]+)\s*,\s*([0-9%]+)\s*\)/iS", $tokens[$i][2], $m))
332                                        {
333                                        for ($i2 = 1, $l2 = count($m); $i2 < $l2; $i2++)
334                                                {
335                                                if (strpos("%", $m[$i2]) !== false)
336                                                        {
337                                                        $m[$i2] = substr($m[$i2], 0, -1);
338                                                        $m[$i2] = (int) (256 * ($m[$i2] / 100));
339                                                        }
340                                                $m[$i2] = str_pad(dechex($m[$i2]),  2, "0", STR_PAD_LEFT);
341                                                }
342                                        $tokens[$i][2] = str_replace($m[0], "#" . $m[1] . $m[2] . $m[3], $tokens[$i][2]);
343                                        }
344                                // Compress color values ("#aabbcc" to "#abc")
345                                if ($sCompressColorValues && preg_match("/\#([0-9a-f]{6})/iS", $tokens[$i][2], $m))
346                                        {
347                                        $m[1] = strtolower($m[1]);
348                                        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))
349                                                {
350                                                $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]);
351                                                }
352                                        }
353                                }
354                        }
355                // Create minified css
356                $r = "";
357                for($i = 0, $l = count($tokens); $i < $l; $i++)
358                        {
359                        // T_AT_RULE
360                        if ($tokens[$i][0] == self::T_AT_RULE)
361                                {
362                                $r .= "@" . $tokens[$i][1] . " " . $tokens[$i][2] . ";";
363                                }
364                        // T_AT_MEDIA_START
365                        elseif ($tokens[$i][0] == self::T_AT_MEDIA_START)
366                                {
367                                if (count($tokens[$i][1]) == 1 && $tokens[$i][1][0] == "all")
368                                        {
369                                        $r .= "@media{";
370                                        }
371                                else
372                                        {
373                                        $r .= "@media " . implode(",", $tokens[$i][1]) . "{";
374                                        }
375                                }
376                        // T_AT_FONT_FACE_START
377                        elseif ($tokens[$i][0] == self::T_AT_FONT_FACE_START)
378                                {
379                                $r .= "@font-face{";
380                                }
381                        // T_FONT_FACE_DECLARATION
382                        elseif ($tokens[$i][0] == self::T_FONT_FACE_DECLARATION)
383                                {
384                                $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_AT_FONT_FACE_END ? "" : ";");
385                                }
386                        // T_AT_PAGE_START
387                        elseif ($tokens[$i][0] == self::T_AT_PAGE_START)
388                                {
389                                $r .= "@page{";
390                                }
391                        // T_PAGE_DECLARATION
392                        elseif ($tokens[$i][0] == self::T_PAGE_DECLARATION)
393                                {
394                                $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_AT_PAGE_END ? "" : ";");
395                                }
396                        // T_SELECTORS
397                        elseif ($tokens[$i][0] == self::T_SELECTORS)
398                                {
399                                $r .= implode(",", $tokens[$i][1]);
400                                }
401                        // Start of declarations
402                        elseif ($tokens[$i][0] == self::T_DECLARATIONS_START)
403                                {
404                                $r .=  "{";
405                                }
406                        // T_DECLARATION
407                        elseif ($tokens[$i][0] == self::T_DECLARATION)
408                                {
409                                if ($sConvertCss3Properties && isset(self::$transformations[$tokens[$i][1]]))
410                                        {
411                                        foreach (self::$transformations[$tokens[$i][1]] as $value)
412                                                {
413                                                if (!is_array($value))
414                                                        {
415                                                        $r .= $value . ":" . $tokens[$i][2] . ";";
416                                                        }
417                                                elseif (is_array($value) && is_callable($value))
418                                                        {
419                                                        $r.= call_user_func_array($value, array($tokens[$i][1], $tokens[$i][2]));
420                                                       
421                                                        }
422                                                }
423                                        }
424                                $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_DECLARATIONS_END ? "" : ";");
425                                }
426                        // T_DECLARATIONS_END, T_AT_MEDIA_END, T_AT_FONT_FACE_END, T_AT_PAGE_END
427                        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)))
428                                {
429                                $r .= "}";
430                                }
431                        else
432                                {
433                                // Tokens with no output:
434                                // T_COMMENT
435                                // T_RULESET_START
436                                // T_RULESET_END
437                                // T_AT_VARIABLES_START
438                                // T_VARIABLE_DECLARATION
439                                // T_AT_VARIABLES_END
440                                }
441                        }
442                return $r;
443                }
444        /**
445         * Parses the Css and returns a array of tokens.
446         *
447         * @param string $css
448         * @return array
449         */
450        public static function parse($css)
451                {
452                // Settings
453                $sDefaultScope          = array("all");                                         // Default scope
454                $sDefaultTrim           = " \t\n\r\0\x0B";                                      // Default trim charlist
455                $sTokenChars            = "@{}();:\n\"'/*,";                            // Tokens triggering parser processing
456                // Basic variables
457                $c                                      = null;                                                         // Current char
458                $p                                      = null;                                                         // Previous char
459                $buffer                         = "";                                                           // Buffer
460                $state                          = array(self::T_DOCUMENT);                      // State stack
461                $currentState           = self::T_DOCUMENT;                                     // Current state
462                $scope                          = $sDefaultScope;                                       // Current scope
463                $stringChar                     = null;                                                         // String delimiter char
464                $isFilterWs                     = true;                                                         // Filter double whitespaces?
465                $selectors                      = array();                                                      // Array with collected selectors
466                $r                                      = array();                                                      // Return value
467                // Prepare css
468                $css                            = str_replace("\r\n", "\n", $css);      // Windows to Unix line endings
469                $css                            = str_replace("\r", "\n", $css);        // Mac to Unix line endings
470                while (strpos($css, "\n\n") !== false)
471                        {
472                        $css                    = str_replace("\n\n", "\n", $css);      // Remove double line endings
473                        }
474                $css                            = str_replace("\t", " ", $css);         // Convert tabs to spaces
475                // Parse css
476                for ($i = 0, $l = strlen($css); $i < $l; $i++)
477                        {
478                        $c = substr($css, $i, 1);
479                        // Filter out double spaces
480                        if ($isFilterWs && $c == " " && $c == $p)
481                                {
482                                continue;
483                                }
484                        $buffer .= $c;
485                        if (strpos($sTokenChars, $c) !== false)
486                                {
487                                //
488                                $currentState   = $state[count($state) - 1];
489                                /*
490                                 * Start of comment
491                                 */
492                                if ($p == "/" && $c == "*" && $currentState != self::T_STRING && $currentState != self::T_COMMENT)
493                                        {
494                                        $saveBuffer = substr($buffer, 0, -2); // save the buffer (will get restored with comment ending)
495                                        $buffer         = $c;
496                                        $isFilterWs     = false;
497                                        array_push($state, self::T_COMMENT);
498                                        }
499                                /*
500                                 * End of comment
501                                 */
502                                elseif ($p == "*" && $c == "/" && $currentState == self::T_COMMENT)
503                                        {
504                                        $r[]            = array(self::T_COMMENT, trim($buffer));
505                                        $buffer         = $saveBuffer;
506                                        $isFilterWs     = true;
507                                        array_pop($state);
508                                        }
509                                /*
510                                 * Start of string
511                                 */
512                                elseif (($c == "\"" || $c == "'") && $currentState != self::T_STRING && $currentState != self::T_COMMENT && $currentState != self::T_STRING_URL)
513                                        {
514                                        $stringChar     = $c;
515                                        $isFilterWs     = false;
516                                        array_push($state, self::T_STRING);
517                                        }
518                                /**
519                                 * Escaped LF in string => remove escape backslash and LF
520                                 */
521                                elseif ($c == "\n" && $p == "\\" && $currentState == self::T_STRING)
522                                        {
523                                        $buffer = substr($buffer, 0, -2);
524                                        }
525                                /*
526                                 * End of string
527                                 */
528                                elseif ($c === $stringChar && $currentState == self::T_STRING)
529                                        {
530                                        if ($p == "\\") // Previous char is a escape char
531                                                {
532                                                $count = 1; 
533                                                $i2 = $i -2;
534                                                while (substr($css, $i2, 1) == "\\")
535                                                        {
536                                                        $count++;
537                                                        $i2--;
538                                                        }
539                                                // if count of escape chars is uneven => continue with string...
540                                                if ($count % 2)
541                                                        {
542                                                        continue;
543                                                        }
544                                                }
545                                        // ...else end the string
546                                        $isFilterWs     = true;
547                                        array_pop($state);
548                                        $stringChar = null;
549                                        }
550                                /**
551                                 * Start of url string property
552                                 */
553                                elseif ($c == "(" && ($currentState != self::T_COMMENT && $currentState != self::T_STRING) && strtolower(substr($css, $i - 3, 3) == "url") 
554                                        && ($currentState == self::T_DECLARATION || $currentState == self::T_FONT_FACE_DECLARATION || $currentState == self::T_PAGE_DECLARATION || $currentState == self::T_VARIABLE_DECLARATION))
555                                        {
556                                        array_push($state, self::T_STRING_URL);
557                                        }
558                                /**
559                                 * End of url string property
560                                 */
561                                elseif (($c == ")" || $c == "\n") && ($currentState != self::T_COMMENT && $currentState != self::T_STRING) && $currentState == self::T_STRING_URL)
562                                        {
563                                        if ($p == "\\")
564                                                {
565                                                continue;
566                                                }
567                                        array_pop($state);
568                                        }
569                                /*
570                                 * Start of at-rule @media block
571                                 */
572                                elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 6)) == "@media")
573                                        {
574                                        $i                      = $i + 6;
575                                        $buffer         = "";
576                                        array_push($state, self::T_AT_MEDIA_START);
577                                        }
578                                /*
579                                 * At-rule @media block media types
580                                 */
581                                elseif ($c == "{" && $currentState == self::T_AT_MEDIA_START)
582                                        {
583                                        $buffer         = strtolower(trim($buffer, $sDefaultTrim . "{"));
584                                        $scope          = $buffer != "" ? array_filter(array_map("trim", explode(",", $buffer))) : $sDefaultScope;
585                                        $r[]            = array(self::T_AT_MEDIA_START, $scope);
586                                        $i                      = $i++;
587                                        $buffer         = "";
588                                        array_pop($state);
589                                        array_push($state, self::T_AT_MEDIA);
590                                        }
591                                /*
592                                 * End of at-rule @media block
593                                 */
594                                elseif ($currentState == self::T_AT_MEDIA && $c == "}")
595                                        {
596                                        $r[]            = array(self::T_AT_MEDIA_END);
597                                        $scope          = $sDefaultScope;
598                                        $buffer         = "";
599                                        array_pop($state);
600                                        }
601                                /*
602                                 * Start of at-rule @font-face block
603                                 */
604                                elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 10)) == "@font-face")
605                                        {
606                                        $r[]            = array(self::T_AT_FONT_FACE_START);
607                                        $i                      = $i + 10;
608                                        $buffer         = "";
609                                        array_push($state, self::T_AT_FONT_FACE);
610                                        }
611                                /*
612                                 * @font-face declaration: Property
613                                 */
614                                elseif ($c == ":" && $currentState == self::T_AT_FONT_FACE)
615                                        {
616                                        $property       = trim($buffer, $sDefaultTrim . ":{");
617                                        $buffer         = "";
618                                        array_push($state, self::T_FONT_FACE_DECLARATION);
619                                        }
620                                /*
621                                 * @font-face declaration: Value
622                                 */
623                                elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_FONT_FACE_DECLARATION)
624                                        {
625                                        $value          = trim($buffer, $sDefaultTrim . ";}");
626                                        $r[]            = array(self::T_FONT_FACE_DECLARATION, $property, $value, $scope);
627                                        $buffer         = "";
628                                        array_pop($state);
629                                        if ($c == "}") // @font-face declaration closed with a right curly brace => closes @font-face block
630                                                {
631                                                array_pop($state);
632                                                $r[]    = array(self::T_AT_FONT_FACE_END);
633                                                }
634                                        }
635                                /*
636                                 * End of at-rule @font-face block
637                                 */
638                                elseif ($c == "}" && $currentState == self::T_AT_FONT_FACE)
639                                        {
640                                        $r[]            = array(self::T_AT_FONT_FACE_END);
641                                        $buffer         = "";
642                                        array_pop($state);
643                                        }
644                                /*
645                                 * Start of at-rule @page block
646                                 */
647                                elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 5)) == "@page")
648                                        {
649                                        $r[]            = array(self::T_AT_PAGE_START);
650                                        $i                      = $i + 5;
651                                        $buffer         = "";
652                                        array_push($state, self::T_AT_PAGE);
653                                        }
654                                /*
655                                 * @page declaration: Property
656                                 */
657                                elseif ($c == ":" && $currentState == self::T_AT_PAGE)
658                                        {
659                                        $property       = trim($buffer, $sDefaultTrim . ":{");
660                                        $buffer         = "";
661                                        array_push($state, self::T_PAGE_DECLARATION);
662                                        }
663                                /*
664                                 * @page declaration: Value
665                                 */
666                                elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_PAGE_DECLARATION)
667                                        {
668                                        $value          = trim($buffer, $sDefaultTrim . ";}");
669                                        $r[]            = array(self::T_PAGE_DECLARATION, $property, $value, $scope);
670                                        $buffer         = "";
671                                        array_pop($state);
672                                        if ($c == "}") // @page declaration closed with a right curly brace => closes @font-face block
673                                                {
674                                                array_pop($state);
675                                                $r[]    = array(self::T_AT_PAGE_END);
676                                                }
677                                        }
678                                /*
679                                 * End of at-rule @page block
680                                 */
681                                elseif ($c == "}" && $currentState == self::T_AT_PAGE)
682                                        {
683                                        $r[]            = array(self::T_AT_PAGE_END);
684                                        $buffer         = "";
685                                        array_pop($state);
686                                        }
687                                /*
688                                 * Start of at-rule @variables block
689                                 */
690                                elseif ($c == "@" && $currentState == self::T_DOCUMENT &&  strtolower(substr($css, $i, 10)) == "@variables")
691                                        {
692                                        $i                      = $i + 10;
693                                        $buffer         = "";
694                                        array_push($state, self::T_AT_VARIABLES_START);
695                                        }
696                                /*
697                                 * @variables media types
698                                 */
699                                elseif ($c == "{" && $currentState == self::T_AT_VARIABLES_START)
700                                        {
701                                        $buffer         = strtolower(trim($buffer, $sDefaultTrim . "{"));
702                                        $r[]            = array(self::T_AT_VARIABLES_START, $scope);
703                                        $scope          = $buffer != "" ? array_filter(array_map("trim", explode(",", $buffer))) : $sDefaultScope;
704                                        $i                      = $i++;
705                                        $buffer         = "";
706                                        array_pop($state);
707                                        array_push($state, self::T_AT_VARIABLES);
708                                        }
709                                /*
710                                 * @variables declaration: Property
711                                 */
712                                elseif ($c == ":" && $currentState == self::T_AT_VARIABLES)
713                                        {
714                                        $property       = trim($buffer, $sDefaultTrim . ":");
715                                        $buffer         = "";
716                                        array_push($state, self::T_VARIABLE_DECLARATION);
717                                        }
718                                /*
719                                 * @variables declaration: Value
720                                 */
721                                elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_VARIABLE_DECLARATION)
722                                        {
723                                        $value          = trim($buffer, $sDefaultTrim . ";}");
724                                        $r[]            = array(self::T_VARIABLE_DECLARATION, $property, $value, $scope);
725                                        $buffer         = "";
726                                        array_pop($state);
727                                        if ($c == "}") // @variable declaration closed with a right curly brace => closes @variables block
728                                                {
729                                                array_pop($state);
730                                                $r[]    = array(self::T_AT_VARIABLES_END);
731                                                $scope  = $sDefaultScope;
732                                                }
733                                        }
734                                /*
735                                 * End of at-rule @variables block
736                                 */
737                                elseif ($c == "}" && $currentState == self::T_AT_VARIABLES)
738                                        {
739                                        $r[]            = array(self::T_AT_VARIABLES_END);
740                                        $scope          = $sDefaultScope;
741                                        $buffer         = "";
742                                        array_pop($state);
743                                        }
744                                /*
745                                 * Start of document level at-rule
746                                 */
747                                elseif ($c == "@" && $currentState == self::T_DOCUMENT)
748                                        {
749                                        $buffer         = "";
750                                        array_push($state, self::T_AT_RULE);
751                                        }
752                                /*
753                                 * End of document level at-rule
754                                 */
755                                elseif ($c == ";" && $currentState == self::T_AT_RULE)
756                                        {
757                                        $pos            = strpos($buffer, " ");
758                                        $rule           = substr($buffer, 0, $pos);
759                                        $value          = trim(substr($buffer, $pos), $sDefaultTrim . ";");
760                                        $r[]            = array(self::T_AT_RULE, $rule, $value);
761                                        $buffer         = "";
762                                        array_pop($state);
763                                        }
764                                /**
765                                 * Selector
766                                 */
767                                elseif ($c == "," && ($currentState == self::T_AT_MEDIA || $currentState ==  self::T_DOCUMENT))
768                                        {
769                                        $selectors[]= trim($buffer, $sDefaultTrim . ",");
770                                        $buffer         = "";
771                                        }
772                                /*
773                                 * Start of ruleset
774                                 */
775                                elseif ($c == "{" && ($currentState == self::T_AT_MEDIA || $currentState == self::T_DOCUMENT))
776                                        {
777                                        $selectors[]= trim($buffer, $sDefaultTrim . "{");
778                                        $selectors      = array_filter(array_map("trim", $selectors));
779                                        $r[]            = array(self::T_RULESET_START);
780                                        $r[]            = array(self::T_SELECTORS, $selectors);
781                                        $r[]            = array(self::T_DECLARATIONS_START);
782                                        $buffer         = "";
783                                        $selectors      = array();
784                                        array_push($state, self::T_DECLARATIONS);
785                                        }
786                                /*
787                                 * Declaration: Property
788                                 */
789                                elseif ($c == ":" && $currentState == self::T_DECLARATIONS)
790                                        {
791                                        $property       = trim($buffer, $sDefaultTrim . ":;");
792                                        $buffer         = "";
793                                        array_push($state, self::T_DECLARATION);
794                                        }
795                                /*
796                                 * Declaration: Value
797                                 */
798                                elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_DECLARATION)
799                                        {
800                                        $value          = trim($buffer, $sDefaultTrim . ";}");
801                                        $r[]            = array(self::T_DECLARATION, $property, $value, $scope);
802                                        $buffer         = "";
803                                        array_pop($state);
804                                        if ($c == "}") // declaration closed with a right curly brace => close ruleset
805                                                {
806                                                array_pop($state);
807                                                $r[]    = array(self::T_DECLARATIONS_END);
808                                                $r[]    = array(self::T_RULESET_END);
809                                                }
810                                        }
811                                /*
812                                 * End of ruleset
813                                 */
814                                elseif ($c == "}" && $currentState == self::T_DECLARATIONS)
815                                        {
816                                        $r[]            = array(self::T_DECLARATIONS_END);
817                                        $r[]            = array(self::T_RULESET_END);
818                                        $buffer         = "";
819                                        array_pop($state);
820                                        }
821                               
822                                }
823                       
824                        $p = $c;
825                        }
826                return $r;
827                }
828        /**
829         * Transforms "opacity: {value}" into browser specific counterparts.
830         *
831         * @param string $property
832         * @param string $value
833         * @return string
834         */
835        private static function _tOpacity($property, $value)
836                {
837                $ieValue = (int) ((float) $value * 100);
838                $r  = "-moz-opacity:" . $value . ";";                                           // Firefox < 3.5
839                $r .= "-ms-filter: \"alpha(opacity=" . $ieValue . ")\";";       // Internet Explorer 8
840                $r .= "filter: alpha(opacity=" . $ieValue . ");zoom: 1;";       // Internet Explorer 4 - 7
841                return $r;
842                }
843        /**
844         * Transforms "white-space: pre-wrap" into browser specific counterparts.
845         *
846         * @param string $property
847         * @param string $value
848         * @return string
849         */
850        private static function _tWhiteSpacePreWrap($property, $value)
851                {
852                if (strtolower($value) == "pre-wrap")
853                        {
854                        $r  = "white-space:-moz-pre-wrap;";             // Mozilla
855                        $r .= "white-space:-webkit-pre-wrap;";  // Webkit
856                        $r .= "white-space:-khtml-pre-wrap;";   // khtml
857                        $r .= "white-space:-pre-wrap;";                 // Opera 4 - 6
858                        $r .= "white-space:-o-pre-wrap;";               // Opera 7+
859                        $r .= "word-wrap:break-word;";                  // Internet Explorer 5.5+
860                        return $r;
861                        }
862                else
863                        {
864                        return "";
865                        }
866                }
867        }
868?>
Note: See TracBrowser for help on using the repository browser.