Gentics Portal.Node PHP API
 All Classes Namespaces Functions Variables Pages
cssmin.php
1 <?php
2 /**
3  * CssMin - A (simple) css minifier with benefits
4  *
5  * --
6  * Copyright (c) 2011 Joe Scylla <joe.scylla@gmail.com>
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  * --
26  *
27  * @package CssMin
28  * @link http://code.google.com/p/cssmin/
29  * @author Joe Scylla <joe.scylla@gmail.com>
30  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
31  * @license http://opensource.org/licenses/mit-license.php MIT License
32  * @version 3.0.1
33  */
34 /**
35  * Abstract definition of a CSS token class.
36  *
37  * Every token has to extend this class.
38  *
39  * @package CssMin/Tokens
40  * @link http://code.google.com/p/cssmin/
41  * @author Joe Scylla <joe.scylla@gmail.com>
42  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
43  * @license http://opensource.org/licenses/mit-license.php MIT License
44  * @version 3.0.1
45  */
46 abstract class aCssToken
47  {
48  /**
49  * Returns the token as string.
50  *
51  * @return string
52  */
53  abstract public function __toString();
54  }
55 
56 /**
57  * Abstract definition of a for a ruleset start token.
58  *
59  * @package CssMin/Tokens
60  * @link http://code.google.com/p/cssmin/
61  * @author Joe Scylla <joe.scylla@gmail.com>
62  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
63  * @license http://opensource.org/licenses/mit-license.php MIT License
64  * @version 3.0.1
65  */
66 abstract class aCssRulesetStartToken extends aCssToken
67  {
68 
69  }
70 
71 /**
72  * Abstract definition of a for ruleset end token.
73  *
74  * @package CssMin/Tokens
75  * @link http://code.google.com/p/cssmin/
76  * @author Joe Scylla <joe.scylla@gmail.com>
77  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
78  * @license http://opensource.org/licenses/mit-license.php MIT License
79  * @version 3.0.1
80  */
81 abstract class aCssRulesetEndToken extends aCssToken
82  {
83  /**
84  * Implements {@link aCssToken::__toString()}.
85  *
86  * @return string
87  */
88  public function __toString()
89  {
90  return "}";
91  }
92  }
93 
94 /**
95  * Abstract definition of a parser plugin.
96  *
97  * Every parser plugin have to extend this class. A parser plugin contains the logic to parse one or aspects of a
98  * stylesheet.
99  *
100  * @package CssMin/Parser/Plugins
101  * @link http://code.google.com/p/cssmin/
102  * @author Joe Scylla <joe.scylla@gmail.com>
103  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
104  * @license http://opensource.org/licenses/mit-license.php MIT License
105  * @version 3.0.1
106  */
107 abstract class aCssParserPlugin
108  {
109  /**
110  * Plugin configuration.
111  *
112  * @var array
113  */
114  protected $configuration = array();
115  /**
116  * The CssParser of the plugin.
117  *
118  * @var CssParser
119  */
120  protected $parser = null;
121  /**
122  * Plugin buffer.
123  *
124  * @var string
125  */
126  protected $buffer = "";
127  /**
128  * Constructor.
129  *
130  * @param CssParser $parser The CssParser object of this plugin.
131  * @param array $configuration Plugin configuration [optional]
132  * @return void
133  */
134  public function __construct(CssParser $parser, array $configuration = null)
135  {
136  $this->configuration = $configuration;
137  $this->parser = $parser;
138  }
139  /**
140  * Returns the array of chars triggering the parser plugin.
141  *
142  * @return array
143  */
144  abstract public function getTriggerChars();
145  /**
146  * Returns the array of states triggering the parser plugin or FALSE if every state will trigger the parser plugin.
147  *
148  * @return array
149  */
150  abstract public function getTriggerStates();
151  /**
152  * Parser routine of the plugin.
153  *
154  * @param integer $index Current index
155  * @param string $char Current char
156  * @param string $previousChar Previous char
157  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
158  */
159  abstract public function parse($index, $char, $previousChar, $state);
160  }
161 
162 /**
163  * Abstract definition of a minifier plugin class.
164  *
165  * Minifier plugin process the parsed tokens one by one to apply changes to the token. Every minifier plugin has to
166  * extend this class.
167  *
168  * @package CssMin/Minifier/Plugins
169  * @link http://code.google.com/p/cssmin/
170  * @author Joe Scylla <joe.scylla@gmail.com>
171  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
172  * @license http://opensource.org/licenses/mit-license.php MIT License
173  * @version 3.0.1
174  */
175 abstract class aCssMinifierPlugin
176  {
177  /**
178  * Plugin configuration.
179  *
180  * @var array
181  */
182  protected $configuration = array();
183  /**
184  * The CssMinifier of the plugin.
185  *
186  * @var CssMinifier
187  */
188  protected $minifier = null;
189  /**
190  * Constructor.
191  *
192  * @param CssMinifier $minifier The CssMinifier object of this plugin.
193  * @param array $configuration Plugin configuration [optional]
194  * @return void
195  */
196  public function __construct(CssMinifier $minifier, array $configuration = array())
197  {
198  $this->configuration = $configuration;
199  $this->minifier = $minifier;
200  }
201  /**
202  * Apply the plugin to the token.
203  *
204  * @param aCssToken $token Token to process
205  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
206  */
207  abstract public function apply(aCssToken &$token);
208  /**
209  * --
210  *
211  * @return array
212  */
213  abstract public function getTriggerTokens();
214  }
215 
216 /**
217  * Abstract definition of a minifier filter class.
218  *
219  * Minifier filters allows a pre-processing of the parsed token to add, edit or delete tokens. Every minifier filter
220  * has to extend this class.
221  *
222  * @package CssMin/Minifier/Filters
223  * @link http://code.google.com/p/cssmin/
224  * @author Joe Scylla <joe.scylla@gmail.com>
225  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
226  * @license http://opensource.org/licenses/mit-license.php MIT License
227  * @version 3.0.1
228  */
229 abstract class aCssMinifierFilter
230  {
231  /**
232  * Filter configuration.
233  *
234  * @var array
235  */
236  protected $configuration = array();
237  /**
238  * The CssMinifier of the filter.
239  *
240  * @var CssMinifier
241  */
242  protected $minifier = null;
243  /**
244  * Constructor.
245  *
246  * @param CssMinifier $minifier The CssMinifier object of this plugin.
247  * @param array $configuration Filter configuration [optional]
248  * @return void
249  */
250  public function __construct(CssMinifier $minifier, array $configuration = array())
251  {
252  $this->configuration = $configuration;
253  $this->minifier = $minifier;
254  }
255  /**
256  * Filter the tokens.
257  *
258  * @param array $tokens Array of objects of type aCssToken
259  * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
260  */
261  abstract public function apply(array &$tokens);
262  }
263 
264 /**
265  * Abstract formatter definition.
266  *
267  * Every formatter have to extend this class.
268  *
269  * @package CssMin/Formatter
270  * @link http://code.google.com/p/cssmin/
271  * @author Joe Scylla <joe.scylla@gmail.com>
272  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
273  * @license http://opensource.org/licenses/mit-license.php MIT License
274  * @version 3.0.1
275  */
276 abstract class aCssFormatter
277  {
278  /**
279  * Indent string.
280  *
281  * @var string
282  */
283  protected $indent = " ";
284  /**
285  * Declaration padding.
286  *
287  * @var integer
288  */
289  protected $padding = 0;
290  /**
291  * Tokens.
292  *
293  * @var array
294  */
295  protected $tokens = array();
296  /**
297  * Constructor.
298  *
299  * @param array $tokens Array of CssToken
300  * @param string $indent Indent string [optional]
301  * @param integer $padding Declaration value padding [optional]
302  */
303  public function __construct(array $tokens, $indent = null, $padding = null)
304  {
305  $this->tokens = $tokens;
306  $this->indent = !is_null($indent) ? $indent : $this->indent;
307  $this->padding = !is_null($padding) ? $padding : $this->padding;
308  }
309  /**
310  * Returns the array of aCssToken as formatted string.
311  *
312  * @return string
313  */
314  abstract public function __toString();
315  }
316 
317 /**
318  * Abstract definition of a ruleset declaration token.
319  *
320  * @package CssMin/Tokens
321  * @link http://code.google.com/p/cssmin/
322  * @author Joe Scylla <joe.scylla@gmail.com>
323  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
324  * @license http://opensource.org/licenses/mit-license.php MIT License
325  * @version 3.0.1
326  */
327 abstract class aCssDeclarationToken extends aCssToken
328  {
329  /**
330  * Is the declaration flagged as important?
331  *
332  * @var boolean
333  */
334  public $IsImportant = false;
335  /**
336  * Is the declaration flagged as last one of the ruleset?
337  *
338  * @var boolean
339  */
340  public $IsLast = false;
341  /**
342  * Property name of the declaration.
343  *
344  * @var string
345  */
346  public $Property = "";
347  /**
348  * Value of the declaration.
349  *
350  * @var string
351  */
352  public $Value = "";
353  /**
354  * Set the properties of the @font-face declaration.
355  *
356  * @param string $property Property of the declaration
357  * @param string $value Value of the declaration
358  * @param boolean $isImportant Is the !important flag is set?
359  * @param boolean $IsLast Is the declaration the last one of the block?
360  * @return void
361  */
362  public function __construct($property, $value, $isImportant = false, $isLast = false)
363  {
364  $this->Property = $property;
365  $this->Value = $value;
366  $this->IsImportant = $isImportant;
367  $this->IsLast = $isLast;
368  }
369  /**
370  * Implements {@link aCssToken::__toString()}.
371  *
372  * @return string
373  */
374  public function __toString()
375  {
376  return $this->Property . ":" . $this->Value . ($this->IsImportant ? " !important" : "") . ($this->IsLast ? "" : ";");
377  }
378  }
379 
380 /**
381  * Abstract definition of a for at-rule block start token.
382  *
383  * @package CssMin/Tokens
384  * @link http://code.google.com/p/cssmin/
385  * @author Joe Scylla <joe.scylla@gmail.com>
386  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
387  * @license http://opensource.org/licenses/mit-license.php MIT License
388  * @version 3.0.1
389  */
390 abstract class aCssAtBlockStartToken extends aCssToken
391  {
392 
393  }
394 
395 /**
396  * Abstract definition of a for at-rule block end token.
397  *
398  * @package CssMin/Tokens
399  * @link http://code.google.com/p/cssmin/
400  * @author Joe Scylla <joe.scylla@gmail.com>
401  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
402  * @license http://opensource.org/licenses/mit-license.php MIT License
403  * @version 3.0.1
404  */
405 abstract class aCssAtBlockEndToken extends aCssToken
406  {
407  /**
408  * Implements {@link aCssToken::__toString()}.
409  *
410  * @return string
411  */
412  public function __toString()
413  {
414  return "}";
415  }
416  }
417 
418 /**
419  * {@link aCssFromatter Formatter} returning the CSS source in {@link http://goo.gl/etzLs Whitesmiths indent style}.
420  *
421  * @package CssMin/Formatter
422  * @link http://code.google.com/p/cssmin/
423  * @author Joe Scylla <joe.scylla@gmail.com>
424  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
425  * @license http://opensource.org/licenses/mit-license.php MIT License
426  * @version 3.0.1
427  */
429  {
430  /**
431  * Implements {@link aCssFormatter::__toString()}.
432  *
433  * @return string
434  */
435  public function __toString()
436  {
437  $r = array();
438  $level = 0;
439  for ($i = 0, $l = count($this->tokens); $i < $l; $i++)
440  {
441  $token = $this->tokens[$i];
442  $class = get_class($token);
443  $indent = str_repeat($this->indent, $level);
444  if ($class === "CssCommentToken")
445  {
446  $lines = array_map("trim", explode("\n", $token->Comment));
447  for ($ii = 0, $ll = count($lines); $ii < $ll; $ii++)
448  {
449  $r[] = $indent . (substr($lines[$ii], 0, 1) == "*" ? " " : "") . $lines[$ii];
450  }
451  }
452  elseif ($class === "CssAtCharsetToken")
453  {
454  $r[] = $indent . "@charset " . $token->Charset . ";";
455  }
456  elseif ($class === "CssAtFontFaceStartToken")
457  {
458  $r[] = $indent . "@font-face";
459  $r[] = $this->indent . $indent . "{";
460  $level++;
461  }
462  elseif ($class === "CssAtImportToken")
463  {
464  $r[] = $indent . "@import " . $token->Import . " " . implode(", ", $token->MediaTypes) . ";";
465  }
466  elseif ($class === "CssAtKeyframesStartToken")
467  {
468  $r[] = $indent . "@keyframes \"" . $token->Name . "\"";
469  $r[] = $this->indent . $indent . "{";
470  $level++;
471  }
472  elseif ($class === "CssAtMediaStartToken")
473  {
474  $r[] = $indent . "@media " . implode(", ", $token->MediaTypes);
475  $r[] = $this->indent . $indent . "{";
476  $level++;
477  }
478  elseif ($class === "CssAtPageStartToken")
479  {
480  $r[] = $indent . "@page";
481  $r[] = $this->indent . $indent . "{";
482  $level++;
483  }
484  elseif ($class === "CssAtVariablesStartToken")
485  {
486  $r[] = $indent . "@variables " . implode(", ", $token->MediaTypes);
487  $r[] = $this->indent . $indent . "{";
488  $level++;
489  }
490  elseif ($class === "CssRulesetStartToken" || $class === "CssAtKeyframesRulesetStartToken")
491  {
492  $r[] = $indent . implode(", ", $token->Selectors);
493  $r[] = $this->indent . $indent . "{";
494  $level++;
495  }
496  elseif ($class == "CssAtFontFaceDeclarationToken"
497  || $class === "CssAtKeyframesRulesetDeclarationToken"
498  || $class === "CssAtPageDeclarationToken"
499  || $class == "CssAtVariablesDeclarationToken"
500  || $class === "CssRulesetDeclarationToken"
501  )
502  {
503  $declaration = $indent . $token->Property . ": ";
504  if ($this->padding)
505  {
506  $declaration = str_pad($declaration, $this->padding, " ", STR_PAD_RIGHT);
507  }
508  $r[] = $declaration . $token->Value . ($token->IsImportant ? " !important" : "") . ";";
509  }
510  elseif ($class === "CssAtFontFaceEndToken"
511  || $class === "CssAtMediaEndToken"
512  || $class === "CssAtKeyframesEndToken"
513  || $class === "CssAtKeyframesRulesetEndToken"
514  || $class === "CssAtPageEndToken"
515  || $class === "CssAtVariablesEndToken"
516  || $class === "CssRulesetEndToken"
517  )
518  {
519  $r[] = $indent . "}";
520  $level--;
521  }
522  }
523  return implode("\n", $r);
524  }
525  }
526 
527 /**
528  * This {@link aCssMinifierPlugin} will process var-statement and sets the declaration value to the variable value.
529  *
530  * This plugin only apply the variable values. The variable values itself will get parsed by the
531  * {@link CssVariablesMinifierFilter}.
532  *
533  * Example:
534  * <code>
535  * @variables
536  * {
537  * defaultColor: black;
538  * }
539  * color: var(defaultColor);
540  * </code>
541  *
542  * Will get converted to:
543  * <code>
544  * color:black;
545  * </code>
546  *
547  * @package CssMin/Minifier/Plugins
548  * @link http://code.google.com/p/cssmin/
549  * @author Joe Scylla <joe.scylla@gmail.com>
550  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
551  * @license http://opensource.org/licenses/mit-license.php MIT License
552  * @version 3.0.1
553  */
555  {
556  /**
557  * Regular expression matching a value.
558  *
559  * @var string
560  */
561  private $reMatch = "/var\((.+)\)/iSU";
562  /**
563  * Parsed variables.
564  *
565  * @var array
566  */
567  private $variables = null;
568  /**
569  * Returns the variables.
570  *
571  * @return array
572  */
573  public function getVariables()
574  {
575  return $this->variables;
576  }
577  /**
578  * Implements {@link aCssMinifierPlugin::minify()}.
579  *
580  * @param aCssToken $token Token to process
581  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
582  */
583  public function apply(aCssToken &$token)
584  {
585  if (stripos($token->Value, "var") !== false && preg_match_all($this->reMatch, $token->Value, $m))
586  {
587  $mediaTypes = $token->MediaTypes;
588  if (!in_array("all", $mediaTypes))
589  {
590  $mediaTypes[] = "all";
591  }
592  for ($i = 0, $l = count($m[0]); $i < $l; $i++)
593  {
594  $variable = trim($m[1][$i]);
595  foreach ($mediaTypes as $mediaType)
596  {
597  if (isset($this->variables[$mediaType], $this->variables[$mediaType][$variable]))
598  {
599  // Variable value found => set the declaration value to the variable value and return
600  $token->Value = str_replace($m[0][$i], $this->variables[$mediaType][$variable], $token->Value);
601  continue 2;
602  }
603  }
604  // If no value was found trigger an error and replace the token with a CssNullToken
605  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": No value found for variable <code>" . $variable . "</code> in media types <code>" . implode(", ", $mediaTypes) . "</code>", (string) $token));
606  $token = new CssNullToken();
607  return true;
608  }
609  }
610  return false;
611  }
612  /**
613  * Implements {@link aMinifierPlugin::getTriggerTokens()}
614  *
615  * @return array
616  */
617  public function getTriggerTokens()
618  {
619  return array
620  (
621  "CssAtFontFaceDeclarationToken",
622  "CssAtPageDeclarationToken",
623  "CssRulesetDeclarationToken"
624  );
625  }
626  /**
627  * Sets the variables.
628  *
629  * @param array $variables Variables to set
630  * @return void
631  */
632  public function setVariables(array $variables)
633  {
634  $this->variables = $variables;
635  }
636  }
637 
638 /**
639  * This {@link aCssMinifierFilter minifier filter} will parse the variable declarations out of @variables at-rule
640  * blocks. The variables will get store in the {@link CssVariablesMinifierPlugin} that will apply the variables to
641  * declaration.
642  *
643  * @package CssMin/Minifier/Filters
644  * @link http://code.google.com/p/cssmin/
645  * @author Joe Scylla <joe.scylla@gmail.com>
646  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
647  * @license http://opensource.org/licenses/mit-license.php MIT License
648  * @version 3.0.1
649  */
651  {
652  /**
653  * Implements {@link aCssMinifierFilter::filter()}.
654  *
655  * @param array $tokens Array of objects of type aCssToken
656  * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
657  */
658  public function apply(array &$tokens)
659  {
660  $variables = array();
661  $defaultMediaTypes = array("all");
662  $mediaTypes = array();
663  $remove = array();
664  for($i = 0, $l = count($tokens); $i < $l; $i++)
665  {
666  // @variables at-rule block found
667  if (get_class($tokens[$i]) === "CssAtVariablesStartToken")
668  {
669  $remove[] = $i;
670  $mediaTypes = (count($tokens[$i]->MediaTypes) == 0 ? $defaultMediaTypes : $tokens[$i]->MediaTypes);
671  foreach ($mediaTypes as $mediaType)
672  {
673  if (!isset($variables[$mediaType]))
674  {
675  $variables[$mediaType] = array();
676  }
677  }
678  // Read the variable declaration tokens
679  for($i = $i; $i < $l; $i++)
680  {
681  // Found a variable declaration => read the variable values
682  if (get_class($tokens[$i]) === "CssAtVariablesDeclarationToken")
683  {
684  foreach ($mediaTypes as $mediaType)
685  {
686  $variables[$mediaType][$tokens[$i]->Property] = $tokens[$i]->Value;
687  }
688  $remove[] = $i;
689  }
690  // Found the variables end token => break;
691  elseif (get_class($tokens[$i]) === "CssAtVariablesEndToken")
692  {
693  $remove[] = $i;
694  break;
695  }
696  }
697  }
698  }
699  // Variables in @variables at-rule blocks
700  foreach($variables as $mediaType => $null)
701  {
702  foreach($variables[$mediaType] as $variable => $value)
703  {
704  // If a var() statement in a variable value found...
705  if (stripos($value, "var") !== false && preg_match_all("/var\((.+)\)/iSU", $value, $m))
706  {
707  // ... then replace the var() statement with the variable values.
708  for ($i = 0, $l = count($m[0]); $i < $l; $i++)
709  {
710  $variables[$mediaType][$variable] = str_replace($m[0][$i], (isset($variables[$mediaType][$m[1][$i]]) ? $variables[$mediaType][$m[1][$i]] : ""), $variables[$mediaType][$variable]);
711  }
712  }
713  }
714  }
715  // Remove the complete @variables at-rule block
716  foreach ($remove as $i)
717  {
718  $tokens[$i] = null;
719  }
720  if (!($plugin = $this->minifier->getPlugin("CssVariablesMinifierPlugin")))
721  {
722  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin <code>CssVariablesMinifierPlugin</code> was not found but is required for <code>" . __CLASS__ . "</code>"));
723  }
724  else
725  {
726  $plugin->setVariables($variables);
727  }
728  return count($remove);
729  }
730  }
731 
732 /**
733  * {@link aCssParserPlugin Parser plugin} for preserve parsing url() values.
734  *
735  * This plugin return no {@link aCssToken CssToken} but ensures that url() values will get parsed properly.
736  *
737  * @package CssMin/Parser/Plugins
738  * @link http://code.google.com/p/cssmin/
739  * @author Joe Scylla <joe.scylla@gmail.com>
740  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
741  * @license http://opensource.org/licenses/mit-license.php MIT License
742  * @version 3.0.1
743  */
745  {
746  /**
747  * Implements {@link aCssParserPlugin::getTriggerChars()}.
748  *
749  * @return array
750  */
751  public function getTriggerChars()
752  {
753  return array("(", ")");
754  }
755  /**
756  * Implements {@link aCssParserPlugin::getTriggerStates()}.
757  *
758  * @return array
759  */
760  public function getTriggerStates()
761  {
762  return false;
763  }
764  /**
765  * Implements {@link aCssParserPlugin::parse()}.
766  *
767  * @param integer $index Current index
768  * @param string $char Current char
769  * @param string $previousChar Previous char
770  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
771  */
772  public function parse($index, $char, $previousChar, $state)
773  {
774  // Start of string
775  if ($char === "(" && strtolower(substr($this->parser->getSource(), $index - 3, 4)) === "url(" && $state !== "T_URL")
776  {
777  $this->parser->pushState("T_URL");
778  $this->parser->setExclusive(__CLASS__);
779  }
780  // Escaped LF in url => remove escape backslash and LF
781  elseif ($char === "\n" && $previousChar === "\\" && $state === "T_URL")
782  {
783  $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2));
784  }
785  // Parse error: Unescaped LF in string literal
786  elseif ($char === "\n" && $previousChar !== "\\" && $state === "T_URL")
787  {
788  $line = $this->parser->getBuffer();
789  $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -1) . ")"); // Replace the LF with the url string delimiter
790  $this->parser->popState();
791  $this->parser->unsetExclusive();
792  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated string literal", $line . "_"));
793  }
794  // End of string
795  elseif ($char === ")" && $state === "T_URL")
796  {
797  $this->parser->popState();
798  $this->parser->unsetExclusive();
799  }
800  else
801  {
802  return false;
803  }
804  return true;
805  }
806  }
807 
808 /**
809  * {@link aCssParserPlugin Parser plugin} for preserve parsing string values.
810  *
811  * This plugin return no {@link aCssToken CssToken} but ensures that string values will get parsed properly.
812  *
813  * @package CssMin/Parser/Plugins
814  * @link http://code.google.com/p/cssmin/
815  * @author Joe Scylla <joe.scylla@gmail.com>
816  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
817  * @license http://opensource.org/licenses/mit-license.php MIT License
818  * @version 3.0.1
819  */
821  {
822  /**
823  * Current string delimiter char.
824  *
825  * @var string
826  */
827  private $delimiterChar = null;
828  /**
829  * Implements {@link aCssParserPlugin::getTriggerChars()}.
830  *
831  * @return array
832  */
833  public function getTriggerChars()
834  {
835  return array("\"", "'", "\n");
836  }
837  /**
838  * Implements {@link aCssParserPlugin::getTriggerStates()}.
839  *
840  * @return array
841  */
842  public function getTriggerStates()
843  {
844  return false;
845  }
846  /**
847  * Implements {@link aCssParserPlugin::parse()}.
848  *
849  * @param integer $index Current index
850  * @param string $char Current char
851  * @param string $previousChar Previous char
852  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
853  */
854  public function parse($index, $char, $previousChar, $state)
855  {
856  // Start of string
857  if (($char === "\"" || $char === "'") && $state !== "T_STRING")
858  {
859  $this->delimiterChar = $char;
860  $this->parser->pushState("T_STRING");
861  $this->parser->setExclusive(__CLASS__);
862  }
863  // Escaped LF in string => remove escape backslash and LF
864  elseif ($char === "\n" && $previousChar === "\\" && $state === "T_STRING")
865  {
866  $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -2));
867  }
868  // Parse error: Unescaped LF in string literal
869  elseif ($char === "\n" && $previousChar !== "\\" && $state === "T_STRING")
870  {
871  $line = $this->parser->getBuffer();
872  $this->parser->popState();
873  $this->parser->unsetExclusive();
874  $this->parser->setBuffer(substr($this->parser->getBuffer(), 0, -1) . $this->delimiterChar); // Replace the LF with the current string char
875  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated string literal", $line . "_"));
876  $this->delimiterChar = null;
877  }
878  // End of string
879  elseif ($char === $this->delimiterChar && $state === "T_STRING")
880  {
881  // If the Previous char is a escape char count the amount of the previous escape chars. If the amount of
882  // escape chars is uneven do not end the string
883  if ($previousChar == "\\")
884  {
885  $source = $this->parser->getSource();
886  $c = 1;
887  $i = $index - 2;
888  while (substr($source, $i, 1) === "\\")
889  {
890  $c++; $i--;
891  }
892  if ($c % 2)
893  {
894  return false;
895  }
896  }
897  $this->parser->popState();
898  $this->parser->unsetExclusive();
899  $this->delimiterChar = null;
900  }
901  else
902  {
903  return false;
904  }
905  return true;
906  }
907  }
908 
909 /**
910  * This {@link aCssMinifierFilter minifier filter} sorts the ruleset declarations of a ruleset by name.
911  *
912  * @package CssMin/Minifier/Filters
913  * @link http://code.google.com/p/cssmin/
914  * @author Rowan Beentje <http://assanka.net>
915  * @copyright Rowan Beentje <http://assanka.net>
916  * @license http://opensource.org/licenses/mit-license.php MIT License
917  * @version 3.0.1
918  */
920  {
921  /**
922  * Implements {@link aCssMinifierFilter::filter()}.
923  *
924  * @param array $tokens Array of objects of type aCssToken
925  * @return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array
926  */
927  public function apply(array &$tokens)
928  {
929  $r = 0;
930  for ($i = 0, $l = count($tokens); $i < $l; $i++)
931  {
932  // Only look for ruleset start rules
933  if (get_class($tokens[$i]) !== "CssRulesetStartToken") { continue; }
934  // Look for the corresponding ruleset end
935  $endIndex = false;
936  for ($ii = $i + 1; $ii < $l; $ii++)
937  {
938  if (get_class($tokens[$ii]) !== "CssRulesetEndToken") { continue; }
939  $endIndex = $ii;
940  break;
941  }
942  if (!$endIndex) { break; }
943  $startIndex = $i;
944  $i = $endIndex;
945  // Skip if there's only one token in this ruleset
946  if ($endIndex - $startIndex <= 2) { continue; }
947  // Ensure that everything between the start and end is a declaration token, for safety
948  for ($ii = $startIndex + 1; $ii < $endIndex; $ii++)
949  {
950  if (get_class($tokens[$ii]) !== "CssRulesetDeclarationToken") { continue(2); }
951  }
952  $declarations = array_slice($tokens, $startIndex + 1, $endIndex - $startIndex - 1);
953  // Check whether a sort is required
954  $sortRequired = $lastPropertyName = false;
955  foreach ($declarations as $declaration)
956  {
957  if ($lastPropertyName)
958  {
959  if (strcmp($lastPropertyName, $declaration->Property) > 0)
960  {
961  $sortRequired = true;
962  break;
963  }
964  }
965  $lastPropertyName = $declaration->Property;
966  }
967  if (!$sortRequired) { continue; }
968  // Arrange the declarations alphabetically by name
969  usort($declarations, array(__CLASS__, "userDefinedSort1"));
970  // Update "IsLast" property
971  for ($ii = 0, $ll = count($declarations) - 1; $ii <= $ll; $ii++)
972  {
973  if ($ii == $ll)
974  {
975  $declarations[$ii]->IsLast = true;
976  }
977  else
978  {
979  $declarations[$ii]->IsLast = false;
980  }
981  }
982  // Splice back into the array.
983  array_splice($tokens, $startIndex + 1, $endIndex - $startIndex - 1, $declarations);
984  $r += $endIndex - $startIndex - 1;
985  }
986  return $r;
987  }
988  /**
989  * User defined sort function.
990  *
991  * @return integer
992  */
993  public static function userDefinedSort1($a, $b)
994  {
995  return strcmp($a->Property, $b->Property);
996  }
997  }
998 
999 /**
1000  * This {@link aCssToken CSS token} represents the start of a ruleset.
1001  *
1002  * @package CssMin/Tokens
1003  * @link http://code.google.com/p/cssmin/
1004  * @author Joe Scylla <joe.scylla@gmail.com>
1005  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1006  * @license http://opensource.org/licenses/mit-license.php MIT License
1007  * @version 3.0.1
1008  */
1010  {
1011  /**
1012  * Array of selectors.
1013  *
1014  * @var array
1015  */
1016  public $Selectors = array();
1017  /**
1018  * Set the properties of a ruleset token.
1019  *
1020  * @param array $selectors Selectors of the ruleset
1021  * @return void
1022  */
1023  public function __construct(array $selectors = array())
1024  {
1025  $this->Selectors = $selectors;
1026  }
1027  /**
1028  * Implements {@link aCssToken::__toString()}.
1029  *
1030  * @return string
1031  */
1032  public function __toString()
1033  {
1034  return implode(",", $this->Selectors) . "{";
1035  }
1036  }
1037 
1038 /**
1039  * {@link aCssParserPlugin Parser plugin} for parsing ruleset block with including declarations.
1040  *
1041  * Found rulesets will add a {@link CssRulesetStartToken} and {@link CssRulesetEndToken} to the
1042  * parser; including declarations as {@link CssRulesetDeclarationToken}.
1043  *
1044  * @package CssMin/Parser/Plugins
1045  * @link http://code.google.com/p/cssmin/
1046  * @author Joe Scylla <joe.scylla@gmail.com>
1047  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1048  * @license http://opensource.org/licenses/mit-license.php MIT License
1049  * @version 3.0.1
1050  */
1052  {
1053  /**
1054  * Implements {@link aCssParserPlugin::getTriggerChars()}.
1055  *
1056  * @return array
1057  */
1058  public function getTriggerChars()
1059  {
1060  return array(",", "{", "}", ":", ";");
1061  }
1062  /**
1063  * Implements {@link aCssParserPlugin::getTriggerStates()}.
1064  *
1065  * @return array
1066  */
1067  public function getTriggerStates()
1068  {
1069  return array("T_DOCUMENT", "T_AT_MEDIA", "T_RULESET::SELECTORS", "T_RULESET", "T_RULESET_DECLARATION");
1070  }
1071  /**
1072  * Selectors.
1073  *
1074  * @var array
1075  */
1076  private $selectors = array();
1077  /**
1078  * Implements {@link aCssParserPlugin::parse()}.
1079  *
1080  * @param integer $index Current index
1081  * @param string $char Current char
1082  * @param string $previousChar Previous char
1083  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
1084  */
1085  public function parse($index, $char, $previousChar, $state)
1086  {
1087  // Start of Ruleset and selectors
1088  if ($char === "," && ($state === "T_DOCUMENT" || $state === "T_AT_MEDIA" || $state === "T_RULESET::SELECTORS"))
1089  {
1090  if ($state !== "T_RULESET::SELECTORS")
1091  {
1092  $this->parser->pushState("T_RULESET::SELECTORS");
1093  }
1094  $this->selectors[] = $this->parser->getAndClearBuffer(",{");
1095  }
1096  // End of selectors and start of declarations
1097  elseif ($char === "{" && ($state === "T_DOCUMENT" || $state === "T_AT_MEDIA" || $state === "T_RULESET::SELECTORS"))
1098  {
1099  if ($this->parser->getBuffer() !== "")
1100  {
1101  $this->selectors[] = $this->parser->getAndClearBuffer(",{");
1102  if ($state == "T_RULESET::SELECTORS")
1103  {
1104  $this->parser->popState();
1105  }
1106  $this->parser->pushState("T_RULESET");
1107  $this->parser->appendToken(new CssRulesetStartToken($this->selectors));
1108  $this->selectors = array();
1109  }
1110  }
1111  // Start of declaration
1112  elseif ($char === ":" && $state === "T_RULESET")
1113  {
1114  $this->parser->pushState("T_RULESET_DECLARATION");
1115  $this->buffer = $this->parser->getAndClearBuffer(":;", true);
1116  }
1117  // Unterminated ruleset declaration
1118  elseif ($char === ":" && $state === "T_RULESET_DECLARATION")
1119  {
1120  // Ignore Internet Explorer filter declarations
1121  if ($this->buffer === "filter")
1122  {
1123  return false;
1124  }
1125  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
1126  }
1127  // End of declaration
1128  elseif (($char === ";" || $char === "}") && $state === "T_RULESET_DECLARATION")
1129  {
1130  $value = $this->parser->getAndClearBuffer(";}");
1131  if (strtolower(substr($value, -10, 10)) === "!important")
1132  {
1133  $value = trim(substr($value, 0, -10));
1134  $isImportant = true;
1135  }
1136  else
1137  {
1138  $isImportant = false;
1139  }
1140  $this->parser->popState();
1141  $this->parser->appendToken(new CssRulesetDeclarationToken($this->buffer, $value, $this->parser->getMediaTypes(), $isImportant));
1142  // Declaration ends with a right curly brace; so we have to end the ruleset
1143  if ($char === "}")
1144  {
1145  $this->parser->appendToken(new CssRulesetEndToken());
1146  $this->parser->popState();
1147  }
1148  $this->buffer = "";
1149  }
1150  // End of ruleset
1151  elseif ($char === "}" && $state === "T_RULESET")
1152  {
1153  $this->parser->popState();
1154  $this->parser->clearBuffer();
1155  $this->parser->appendToken(new CssRulesetEndToken());
1156  $this->buffer = "";
1157  $this->selectors = array();
1158  }
1159  else
1160  {
1161  return false;
1162  }
1163  return true;
1164  }
1165  }
1166 
1167 /**
1168  * This {@link aCssToken CSS token} represents the end of a ruleset.
1169  *
1170  * @package CssMin/Tokens
1171  * @link http://code.google.com/p/cssmin/
1172  * @author Joe Scylla <joe.scylla@gmail.com>
1173  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1174  * @license http://opensource.org/licenses/mit-license.php MIT License
1175  * @version 3.0.1
1176  */
1178  {
1179 
1180  }
1181 
1182 /**
1183  * This {@link aCssToken CSS token} represents a ruleset declaration.
1184  *
1185  * @package CssMin/Tokens
1186  * @link http://code.google.com/p/cssmin/
1187  * @author Joe Scylla <joe.scylla@gmail.com>
1188  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1189  * @license http://opensource.org/licenses/mit-license.php MIT License
1190  * @version 3.0.1
1191  */
1193  {
1194  /**
1195  * Media types of the declaration.
1196  *
1197  * @var array
1198  */
1199  public $MediaTypes = array("all");
1200  /**
1201  * Set the properties of a ddocument- or at-rule @media level declaration.
1202  *
1203  * @param string $property Property of the declaration
1204  * @param string $value Value of the declaration
1205  * @param mixed $mediaTypes Media types of the declaration
1206  * @param boolean $isImportant Is the !important flag is set
1207  * @param boolean $isLast Is the declaration the last one of the ruleset
1208  * @return void
1209  */
1210  public function __construct($property, $value, $mediaTypes = null, $isImportant = false, $isLast = false)
1211  {
1212  parent::__construct($property, $value, $isImportant, $isLast);
1213  $this->MediaTypes = $mediaTypes ? $mediaTypes : array("all");
1214  }
1215  }
1216 
1217 /**
1218  * This {@link aCssMinifierFilter minifier filter} sets the IsLast property of any last declaration in a ruleset,
1219  * @font-face at-rule or @page at-rule block. If the property IsLast is TRUE the decrations will get stringified
1220  * without tailing semicolon.
1221  *
1222  * @package CssMin/Minifier/Filters
1223  * @link http://code.google.com/p/cssmin/
1224  * @author Joe Scylla <joe.scylla@gmail.com>
1225  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1226  * @license http://opensource.org/licenses/mit-license.php MIT License
1227  * @version 3.0.1
1228  */
1230  {
1231  /**
1232  * Implements {@link aCssMinifierFilter::filter()}.
1233  *
1234  * @param array $tokens Array of objects of type aCssToken
1235  * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
1236  */
1237  public function apply(array &$tokens)
1238  {
1239  for ($i = 0, $l = count($tokens); $i < $l; $i++)
1240  {
1241  $current = get_class($tokens[$i]);
1242  $next = isset($tokens[$i+1]) ? get_class($tokens[$i+1]) : false;
1243  if (($current === "CssRulesetDeclarationToken" && $next === "CssRulesetEndToken") ||
1244  ($current === "CssAtFontFaceDeclarationToken" && $next === "CssAtFontFaceEndToken") ||
1245  ($current === "CssAtPageDeclarationToken" && $next === "CssAtPageEndToken"))
1246  {
1247  $tokens[$i]->IsLast = true;
1248  }
1249  }
1250  return 0;
1251  }
1252  }
1253 
1254 /**
1255  * This {@link aCssMinifierFilter minifier filter} will remove any empty rulesets (including @keyframes at-rule block
1256  * rulesets).
1257  *
1258  * @package CssMin/Minifier/Filters
1259  * @link http://code.google.com/p/cssmin/
1260  * @author Joe Scylla <joe.scylla@gmail.com>
1261  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1262  * @license http://opensource.org/licenses/mit-license.php MIT License
1263  * @version 3.0.1
1264  */
1266  {
1267  /**
1268  * Implements {@link aCssMinifierFilter::filter()}.
1269  *
1270  * @param array $tokens Array of objects of type aCssToken
1271  * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
1272  */
1273  public function apply(array &$tokens)
1274  {
1275  $r = 0;
1276  for ($i = 0, $l = count($tokens); $i < $l; $i++)
1277  {
1278  $current = get_class($tokens[$i]);
1279  $next = isset($tokens[$i + 1]) ? get_class($tokens[$i + 1]) : false;
1280  if (($current === "CssRulesetStartToken" && $next === "CssRulesetEndToken") ||
1281  ($current === "CssAtKeyframesRulesetStartToken" && $next === "CssAtKeyframesRulesetEndToken" && !array_intersect(array("from", "0%", "to", "100%"), array_map("strtolower", $tokens[$i]->Selectors)))
1282  )
1283  {
1284  $tokens[$i] = null;
1285  $tokens[$i + 1] = null;
1286  $i++;
1287  $r = $r + 2;
1288  }
1289  }
1290  return $r;
1291  }
1292  }
1293 
1294 /**
1295  * This {@link aCssMinifierFilter minifier filter} will remove any empty @font-face, @keyframes, @media and @page
1296  * at-rule blocks.
1297  *
1298  * @package CssMin/Minifier/Filters
1299  * @link http://code.google.com/p/cssmin/
1300  * @author Joe Scylla <joe.scylla@gmail.com>
1301  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1302  * @license http://opensource.org/licenses/mit-license.php MIT License
1303  * @version 3.0.1
1304  */
1306  {
1307  /**
1308  * Implements {@link aCssMinifierFilter::filter()}.
1309  *
1310  * @param array $tokens Array of objects of type aCssToken
1311  * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
1312  */
1313  public function apply(array &$tokens)
1314  {
1315  $r = 0;
1316  for ($i = 0, $l = count($tokens); $i < $l; $i++)
1317  {
1318  $current = get_class($tokens[$i]);
1319  $next = isset($tokens[$i + 1]) ? get_class($tokens[$i + 1]) : false;
1320  if (($current === "CssAtFontFaceStartToken" && $next === "CssAtFontFaceEndToken") ||
1321  ($current === "CssAtKeyframesStartToken" && $next === "CssAtKeyframesEndToken") ||
1322  ($current === "CssAtPageStartToken" && $next === "CssAtPageEndToken") ||
1323  ($current === "CssAtMediaStartToken" && $next === "CssAtMediaEndToken"))
1324  {
1325  $tokens[$i] = null;
1326  $tokens[$i + 1] = null;
1327  $i++;
1328  $r = $r + 2;
1329  }
1330  }
1331  return $r;
1332  }
1333  }
1334 
1335 /**
1336  * This {@link aCssMinifierFilter minifier filter} will remove any comments from the array of parsed tokens.
1337  *
1338  * @package CssMin/Minifier/Filters
1339  * @link http://code.google.com/p/cssmin/
1340  * @author Joe Scylla <joe.scylla@gmail.com>
1341  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1342  * @license http://opensource.org/licenses/mit-license.php MIT License
1343  * @version 3.0.1
1344  */
1346  {
1347  /**
1348  * Implements {@link aCssMinifierFilter::filter()}.
1349  *
1350  * @param array $tokens Array of objects of type aCssToken
1351  * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
1352  */
1353  public function apply(array &$tokens)
1354  {
1355  $r = 0;
1356  for ($i = 0, $l = count($tokens); $i < $l; $i++)
1357  {
1358  if (get_class($tokens[$i]) === "CssCommentToken")
1359  {
1360  $tokens[$i] = null;
1361  $r++;
1362  }
1363  }
1364  return $r;
1365  }
1366  }
1367 
1368 /**
1369  * CSS Parser.
1370  *
1371  * @package CssMin/Parser
1372  * @link http://code.google.com/p/cssmin/
1373  * @author Joe Scylla <joe.scylla@gmail.com>
1374  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1375  * @license http://opensource.org/licenses/mit-license.php MIT License
1376  * @version 3.0.1
1377  */
1379  {
1380  /**
1381  * Parse buffer.
1382  *
1383  * @var string
1384  */
1385  private $buffer = "";
1386  /**
1387  * {@link aCssParserPlugin Plugins}.
1388  *
1389  * @var array
1390  */
1391  private $plugins = array();
1392  /**
1393  * Source to parse.
1394  *
1395  * @var string
1396  */
1397  private $source = "";
1398  /**
1399  * Current state.
1400  *
1401  * @var integer
1402  */
1403  private $state = "T_DOCUMENT";
1404  /**
1405  * Exclusive state.
1406  *
1407  * @var string
1408  */
1409  private $stateExclusive = false;
1410  /**
1411  * Media types state.
1412  *
1413  * @var mixed
1414  */
1415  private $stateMediaTypes = false;
1416  /**
1417  * State stack.
1418  *
1419  * @var array
1420  */
1421  private $states = array("T_DOCUMENT");
1422  /**
1423  * Parsed tokens.
1424  *
1425  * @var array
1426  */
1427  private $tokens = array();
1428  /**
1429  * Constructer.
1430  *
1431  * Create instances of the used {@link aCssParserPlugin plugins}.
1432  *
1433  * @param string $source CSS source [optional]
1434  * @param array $plugins Plugin configuration [optional]
1435  * @return void
1436  */
1437  public function __construct($source = null, array $plugins = null)
1438  {
1439  $plugins = array_merge(array
1440  (
1441  "Comment" => true,
1442  "String" => true,
1443  "Url" => true,
1444  "Expression" => true,
1445  "Ruleset" => true,
1446  "AtCharset" => true,
1447  "AtFontFace" => true,
1448  "AtImport" => true,
1449  "AtKeyframes" => true,
1450  "AtMedia" => true,
1451  "AtPage" => true,
1452  "AtVariables" => true
1453  ), is_array($plugins) ? $plugins : array());
1454  // Create plugin instances
1455  foreach ($plugins as $name => $config)
1456  {
1457  if ($config !== false)
1458  {
1459  $class = "Css" . $name . "ParserPlugin";
1460  $config = is_array($config) ? $config : array();
1461  if (class_exists($class))
1462  {
1463  $this->plugins[] = new $class($this, $config);
1464  }
1465  else
1466  {
1467  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin <code>" . $name . "</code> with the class name <code>" . $class . "</code> was not found"));
1468  }
1469  }
1470  }
1471  if (!is_null($source))
1472  {
1473  $this->parse($source);
1474  }
1475  }
1476  /**
1477  * Append a token to the array of tokens.
1478  *
1479  * @param aCssToken $token Token to append
1480  * @return void
1481  */
1482  public function appendToken(aCssToken $token)
1483  {
1484  $this->tokens[] = $token;
1485  }
1486  /**
1487  * Clears the current buffer.
1488  *
1489  * @return void
1490  */
1491  public function clearBuffer()
1492  {
1493  $this->buffer = "";
1494  }
1495  /**
1496  * Returns and clear the current buffer.
1497  *
1498  * @param string $trim Chars to use to trim the returned buffer
1499  * @param boolean $tolower if TRUE the returned buffer will get converted to lower case
1500  * @return string
1501  */
1502  public function getAndClearBuffer($trim = "", $tolower = false)
1503  {
1504  $r = $this->getBuffer($trim, $tolower);
1505  $this->buffer = "";
1506  return $r;
1507  }
1508  /**
1509  * Returns the current buffer.
1510  *
1511  * @param string $trim Chars to use to trim the returned buffer
1512  * @param boolean $tolower if TRUE the returned buffer will get converted to lower case
1513  * @return string
1514  */
1515  public function getBuffer($trim = "", $tolower = false)
1516  {
1517  $r = $this->buffer;
1518  if ($trim)
1519  {
1520  $r = trim($r, " \t\n\r\0\x0B" . $trim);
1521  }
1522  if ($tolower)
1523  {
1524  $r = strtolower($r);
1525  }
1526  return $r;
1527  }
1528  /**
1529  * Returns the current media types state.
1530  *
1531  * @return array
1532  */
1533  public function getMediaTypes()
1534  {
1535  return $this->stateMediaTypes;
1536  }
1537  /**
1538  * Returns the CSS source.
1539  *
1540  * @return string
1541  */
1542  public function getSource()
1543  {
1544  return $this->source;
1545  }
1546  /**
1547  * Returns the current state.
1548  *
1549  * @return integer The current state
1550  */
1551  public function getState()
1552  {
1553  return $this->state;
1554  }
1555  /**
1556  * Returns a plugin by class name.
1557  *
1558  * @param string $name Class name of the plugin
1559  * @return aCssParserPlugin
1560  */
1561  public function getPlugin($class)
1562  {
1563  static $index = null;
1564  if (is_null($index))
1565  {
1566  $index = array();
1567  for ($i = 0, $l = count($this->plugins); $i < $l; $i++)
1568  {
1569  $index[get_class($this->plugins[$i])] = $i;
1570  }
1571  }
1572  return isset($index[$class]) ? $this->plugins[$index[$class]] : false;
1573  }
1574  /**
1575  * Returns the parsed tokens.
1576  *
1577  * @return array
1578  */
1579  public function getTokens()
1580  {
1581  return $this->tokens;
1582  }
1583  /**
1584  * Returns if the current state equals the passed state.
1585  *
1586  * @param integer $state State to compare with the current state
1587  * @return boolean TRUE is the state equals to the passed state; FALSE if not
1588  */
1589  public function isState($state)
1590  {
1591  return ($this->state == $state);
1592  }
1593  /**
1594  * Parse the CSS source and return a array with parsed tokens.
1595  *
1596  * @param string $source CSS source
1597  * @return array Array with tokens
1598  */
1599  public function parse($source)
1600  {
1601  // Reset
1602  $this->source = "";
1603  $this->tokens = array();
1604  // Create a global and plugin lookup table for trigger chars; set array of plugins as local variable and create
1605  // several helper variables for plugin handling
1606  $globalTriggerChars = "";
1607  $plugins = $this->plugins;
1608  $pluginCount = count($plugins);
1609  $pluginIndex = array();
1610  $pluginTriggerStates = array();
1611  $pluginTriggerChars = array();
1612  for ($i = 0, $l = count($plugins); $i < $l; $i++)
1613  {
1614  $tPluginClassName = get_class($plugins[$i]);
1615  $pluginTriggerChars[$i] = implode("", $plugins[$i]->getTriggerChars());
1616  $tPluginTriggerStates = $plugins[$i]->getTriggerStates();
1617  $pluginTriggerStates[$i] = $tPluginTriggerStates === false ? false : "|" . implode("|", $tPluginTriggerStates) . "|";
1618  $pluginIndex[$tPluginClassName] = $i;
1619  for ($ii = 0, $ll = strlen($pluginTriggerChars[$i]); $ii < $ll; $ii++)
1620  {
1621  $c = substr($pluginTriggerChars[$i], $ii, 1);
1622  if (strpos($globalTriggerChars, $c) === false)
1623  {
1624  $globalTriggerChars .= $c;
1625  }
1626  }
1627  }
1628  // Normalise line endings
1629  $source = str_replace("\r\n", "\n", $source); // Windows to Unix line endings
1630  $source = str_replace("\r", "\n", $source); // Mac to Unix line endings
1631  $this->source = $source;
1632  // Variables
1633  $buffer = &$this->buffer;
1634  $exclusive = &$this->stateExclusive;
1635  $state = &$this->state;
1636  $c = $p = null;
1637  // --
1638  for ($i = 0, $l = strlen($source); $i < $l; $i++)
1639  {
1640  // Set the current Char
1641  $c = $source[$i]; // Is faster than: $c = substr($source, $i, 1);
1642  // Normalize and filter double whitespace characters
1643  if ($exclusive === false)
1644  {
1645  if ($c === "\n" || $c === "\t")
1646  {
1647  $c = " ";
1648  }
1649  if ($c === " " && $p === " ")
1650  {
1651  continue;
1652  }
1653  }
1654  $buffer .= $c;
1655  // Extended processing only if the current char is a global trigger char
1656  if (strpos($globalTriggerChars, $c) !== false)
1657  {
1658  // Exclusive state is set; process with the exclusive plugin
1659  if ($exclusive)
1660  {
1661  $tPluginIndex = $pluginIndex[$exclusive];
1662  if (strpos($pluginTriggerChars[$tPluginIndex], $c) !== false && ($pluginTriggerStates[$tPluginIndex] === false || strpos($pluginTriggerStates[$tPluginIndex], $state) !== false))
1663  {
1664  $r = $plugins[$tPluginIndex]->parse($i, $c, $p, $state);
1665  // Return value is TRUE => continue with next char
1666  if ($r === true)
1667  {
1668  continue;
1669  }
1670  // Return value is numeric => set new index and continue with next char
1671  elseif ($r !== false && $r != $i)
1672  {
1673  $i = $r;
1674  continue;
1675  }
1676  }
1677  }
1678  // Else iterate through the plugins
1679  else
1680  {
1681  $triggerState = "|" . $state . "|";
1682  for ($ii = 0, $ll = $pluginCount; $ii < $ll; $ii++)
1683  {
1684  // Only process if the current char is one of the plugin trigger chars
1685  if (strpos($pluginTriggerChars[$ii], $c) !== false && ($pluginTriggerStates[$ii] === false || strpos($pluginTriggerStates[$ii], $triggerState) !== false))
1686  {
1687  // Process with the plugin
1688  $r = $plugins[$ii]->parse($i, $c, $p, $state);
1689  // Return value is TRUE => break the plugin loop and and continue with next char
1690  if ($r === true)
1691  {
1692  break;
1693  }
1694  // Return value is numeric => set new index, break the plugin loop and and continue with next char
1695  elseif ($r !== false && $r != $i)
1696  {
1697  $i = $r;
1698  break;
1699  }
1700  }
1701  }
1702  }
1703  }
1704  $p = $c; // Set the parent char
1705  }
1706  return $this->tokens;
1707  }
1708  /**
1709  * Remove the last state of the state stack and return the removed stack value.
1710  *
1711  * @return integer Removed state value
1712  */
1713  public function popState()
1714  {
1715  $r = array_pop($this->states);
1716  $this->state = $this->states[count($this->states) - 1];
1717  return $r;
1718  }
1719  /**
1720  * Adds a new state onto the state stack.
1721  *
1722  * @param integer $state State to add onto the state stack.
1723  * @return integer The index of the added state in the state stacks
1724  */
1725  public function pushState($state)
1726  {
1727  $r = array_push($this->states, $state);
1728  $this->state = $this->states[count($this->states) - 1];
1729  return $r;
1730  }
1731  /**
1732  * Sets/restores the buffer.
1733  *
1734  * @param string $buffer Buffer to set
1735  * @return void
1736  */
1737  public function setBuffer($buffer)
1738  {
1739  $this->buffer = $buffer;
1740  }
1741  /**
1742  * Set the exclusive state.
1743  *
1744  * @param string $exclusive Exclusive state
1745  * @return void
1746  */
1747  public function setExclusive($exclusive)
1748  {
1749  $this->stateExclusive = $exclusive;
1750  }
1751  /**
1752  * Set the media types state.
1753  *
1754  * @param array $mediaTypes Media types state
1755  * @return void
1756  */
1757  public function setMediaTypes(array $mediaTypes)
1758  {
1759  $this->stateMediaTypes = $mediaTypes;
1760  }
1761  /**
1762  * Sets the current state in the state stack; equals to {@link CssParser::popState()} + {@link CssParser::pushState()}.
1763  *
1764  * @param integer $state State to set
1765  * @return integer
1766  */
1767  public function setState($state)
1768  {
1769  $r = array_pop($this->states);
1770  array_push($this->states, $state);
1771  $this->state = $this->states[count($this->states) - 1];
1772  return $r;
1773  }
1774  /**
1775  * Removes the exclusive state.
1776  *
1777  * @return void
1778  */
1779  public function unsetExclusive()
1780  {
1781  $this->stateExclusive = false;
1782  }
1783  /**
1784  * Removes the media types state.
1785  *
1786  * @return void
1787  */
1788  public function unsetMediaTypes()
1789  {
1790  $this->stateMediaTypes = false;
1791  }
1792  }
1793 
1794 /**
1795  * {@link aCssFromatter Formatter} returning the CSS source in {@link http://goo.gl/j4XdU OTBS indent style} (The One True Brace Style).
1796  *
1797  * @package CssMin/Formatter
1798  * @link http://code.google.com/p/cssmin/
1799  * @author Joe Scylla <joe.scylla@gmail.com>
1800  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1801  * @license http://opensource.org/licenses/mit-license.php MIT License
1802  * @version 3.0.1
1803  */
1805  {
1806  /**
1807  * Implements {@link aCssFormatter::__toString()}.
1808  *
1809  * @return string
1810  */
1811  public function __toString()
1812  {
1813  $r = array();
1814  $level = 0;
1815  for ($i = 0, $l = count($this->tokens); $i < $l; $i++)
1816  {
1817  $token = $this->tokens[$i];
1818  $class = get_class($token);
1819  $indent = str_repeat($this->indent, $level);
1820  if ($class === "CssCommentToken")
1821  {
1822  $lines = array_map("trim", explode("\n", $token->Comment));
1823  for ($ii = 0, $ll = count($lines); $ii < $ll; $ii++)
1824  {
1825  $r[] = $indent . (substr($lines[$ii], 0, 1) == "*" ? " " : "") . $lines[$ii];
1826  }
1827  }
1828  elseif ($class === "CssAtCharsetToken")
1829  {
1830  $r[] = $indent . "@charset " . $token->Charset . ";";
1831  }
1832  elseif ($class === "CssAtFontFaceStartToken")
1833  {
1834  $r[] = $indent . "@font-face {";
1835  $level++;
1836  }
1837  elseif ($class === "CssAtImportToken")
1838  {
1839  $r[] = $indent . "@import " . $token->Import . " " . implode(", ", $token->MediaTypes) . ";";
1840  }
1841  elseif ($class === "CssAtKeyframesStartToken")
1842  {
1843  $r[] = $indent . "@keyframes \"" . $token->Name . "\" {";
1844  $level++;
1845  }
1846  elseif ($class === "CssAtMediaStartToken")
1847  {
1848  $r[] = $indent . "@media " . implode(", ", $token->MediaTypes) . " {";
1849  $level++;
1850  }
1851  elseif ($class === "CssAtPageStartToken")
1852  {
1853  $r[] = $indent . "@page {";
1854  $level++;
1855  }
1856  elseif ($class === "CssAtVariablesStartToken")
1857  {
1858  $r[] = $indent . "@variables " . implode(", ", $token->MediaTypes) . " {";
1859  $level++;
1860  }
1861  elseif ($class === "CssRulesetStartToken" || $class === "CssAtKeyframesRulesetStartToken")
1862  {
1863  $r[] = $indent . implode(", ", $token->Selectors) . " {";
1864  $level++;
1865  }
1866  elseif ($class == "CssAtFontFaceDeclarationToken"
1867  || $class === "CssAtKeyframesRulesetDeclarationToken"
1868  || $class === "CssAtPageDeclarationToken"
1869  || $class == "CssAtVariablesDeclarationToken"
1870  || $class === "CssRulesetDeclarationToken"
1871  )
1872  {
1873  $declaration = $indent . $token->Property . ": ";
1874  if ($this->padding)
1875  {
1876  $declaration = str_pad($declaration, $this->padding, " ", STR_PAD_RIGHT);
1877  }
1878  $r[] = $declaration . $token->Value . ($token->IsImportant ? " !important" : "") . ";";
1879  }
1880  elseif ($class === "CssAtFontFaceEndToken"
1881  || $class === "CssAtMediaEndToken"
1882  || $class === "CssAtKeyframesEndToken"
1883  || $class === "CssAtKeyframesRulesetEndToken"
1884  || $class === "CssAtPageEndToken"
1885  || $class === "CssAtVariablesEndToken"
1886  || $class === "CssRulesetEndToken"
1887  )
1888  {
1889  $level--;
1890  $r[] = str_repeat($indent, $level) . "}";
1891  }
1892  }
1893  return implode("\n", $r);
1894  }
1895  }
1896 
1897 /**
1898  * This {@link aCssToken CSS token} is a utility token that extends {@link aNullToken} and returns only a empty string.
1899  *
1900  * @package CssMin/Tokens
1901  * @link http://code.google.com/p/cssmin/
1902  * @author Joe Scylla <joe.scylla@gmail.com>
1903  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1904  * @license http://opensource.org/licenses/mit-license.php MIT License
1905  * @version 3.0.1
1906  */
1908  {
1909  /**
1910  * Implements {@link aCssToken::__toString()}.
1911  *
1912  * @return string
1913  */
1914  public function __toString()
1915  {
1916  return "";
1917  }
1918  }
1919 
1920 /**
1921  * CSS Minifier.
1922  *
1923  * @package CssMin/Minifier
1924  * @link http://code.google.com/p/cssmin/
1925  * @author Joe Scylla <joe.scylla@gmail.com>
1926  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
1927  * @license http://opensource.org/licenses/mit-license.php MIT License
1928  * @version 3.0.1
1929  */
1931  {
1932  /**
1933  * {@link aCssMinifierFilter Filters}.
1934  *
1935  * @var array
1936  */
1937  private $filters = array();
1938  /**
1939  * {@link aCssMinifierPlugin Plugins}.
1940  *
1941  * @var array
1942  */
1943  private $plugins = array();
1944  /**
1945  * Minified source.
1946  *
1947  * @var string
1948  */
1949  private $minified = "";
1950  /**
1951  * Constructer.
1952  *
1953  * Creates instances of {@link aCssMinifierFilter filters} and {@link aCssMinifierPlugin plugins}.
1954  *
1955  * @param string $source CSS source [optional]
1956  * @param array $filters Filter configuration [optional]
1957  * @param array $plugins Plugin configuration [optional]
1958  * @return void
1959  */
1960  public function __construct($source = null, array $filters = null, array $plugins = null)
1961  {
1962  $filters = array_merge(array
1963  (
1964  "ImportImports" => false,
1965  "RemoveComments" => true,
1966  "RemoveEmptyRulesets" => true,
1967  "RemoveEmptyAtBlocks" => true,
1968  "ConvertLevel3Properties" => false,
1969  "ConvertLevel3AtKeyframes" => false,
1970  "Variables" => true,
1971  "RemoveLastDelarationSemiColon" => true
1972  ), is_array($filters) ? $filters : array());
1973  $plugins = array_merge(array
1974  (
1975  "Variables" => true,
1976  "ConvertFontWeight" => false,
1977  "ConvertHslColors" => false,
1978  "ConvertRgbColors" => false,
1979  "ConvertNamedColors" => false,
1980  "CompressColorValues" => false,
1981  "CompressUnitValues" => false,
1982  "CompressExpressionValues" => false
1983  ), is_array($plugins) ? $plugins : array());
1984  // Filters
1985  foreach ($filters as $name => $config)
1986  {
1987  if ($config !== false)
1988  {
1989  $class = "Css" . $name . "MinifierFilter";
1990  $config = is_array($config) ? $config : array();
1991  if (class_exists($class))
1992  {
1993  $this->filters[] = new $class($this, $config);
1994  }
1995  else
1996  {
1997  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The filter <code>" . $name . "</code> with the class name <code>" . $class . "</code> was not found"));
1998  }
1999  }
2000  }
2001  // Plugins
2002  foreach ($plugins as $name => $config)
2003  {
2004  if ($config !== false)
2005  {
2006  $class = "Css" . $name . "MinifierPlugin";
2007  $config = is_array($config) ? $config : array();
2008  if (class_exists($class))
2009  {
2010  $this->plugins[] = new $class($this, $config);
2011  }
2012  else
2013  {
2014  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": The plugin <code>" . $name . "</code> with the class name <code>" . $class . "</code> was not found"));
2015  }
2016  }
2017  }
2018  // --
2019  if (!is_null($source))
2020  {
2021  $this->minify($source);
2022  }
2023  }
2024  /**
2025  * Returns the minified Source.
2026  *
2027  * @return string
2028  */
2029  public function getMinified()
2030  {
2031  return $this->minified;
2032  }
2033  /**
2034  * Returns a plugin by class name.
2035  *
2036  * @param string $name Class name of the plugin
2037  * @return aCssMinifierPlugin
2038  */
2039  public function getPlugin($class)
2040  {
2041  static $index = null;
2042  if (is_null($index))
2043  {
2044  $index = array();
2045  for ($i = 0, $l = count($this->plugins); $i < $l; $i++)
2046  {
2047  $index[get_class($this->plugins[$i])] = $i;
2048  }
2049  }
2050  return isset($index[$class]) ? $this->plugins[$index[$class]] : false;
2051  }
2052  /**
2053  * Minifies the CSS source.
2054  *
2055  * @param string $source CSS source
2056  * @return string
2057  */
2058  public function minify($source)
2059  {
2060  // Variables
2061  $r = "";
2062  $parser = new CssParser($source);
2063  $tokens = $parser->getTokens();
2064  $filters = $this->filters;
2065  $filterCount = count($this->filters);
2066  $plugins = $this->plugins;
2067  $pluginCount = count($plugins);
2068  $pluginIndex = array();
2069  $pluginTriggerTokens = array();
2070  $globalTriggerTokens = array();
2071  for ($i = 0, $l = count($plugins); $i < $l; $i++)
2072  {
2073  $tPluginClassName = get_class($plugins[$i]);
2074  $pluginTriggerTokens[$i] = $plugins[$i]->getTriggerTokens();
2075  foreach ($pluginTriggerTokens[$i] as $v)
2076  {
2077  if (!in_array($v, $globalTriggerTokens))
2078  {
2079  $globalTriggerTokens[] = $v;
2080  }
2081  }
2082  $pluginTriggerTokens[$i] = "|" . implode("|", $pluginTriggerTokens[$i]) . "|";
2083  $pluginIndex[$tPluginClassName] = $i;
2084  }
2085  $globalTriggerTokens = "|" . implode("|", $globalTriggerTokens) . "|";
2086  /*
2087  * Apply filters
2088  */
2089  for($i = 0; $i < $filterCount; $i++)
2090  {
2091  // Apply the filter; if the return value is larger than 0...
2092  if ($filters[$i]->apply($tokens) > 0)
2093  {
2094  // ...then filter null values and rebuild the token array
2095  $tokens = array_values(array_filter($tokens));
2096  }
2097  }
2098  $tokenCount = count($tokens);
2099  /*
2100  * Apply plugins
2101  */
2102  for($i = 0; $i < $tokenCount; $i++)
2103  {
2104  $triggerToken = "|" . get_class($tokens[$i]) . "|";
2105  if (strpos($globalTriggerTokens, $triggerToken) !== false)
2106  {
2107  for($ii = 0; $ii < $pluginCount; $ii++)
2108  {
2109  if (strpos($pluginTriggerTokens[$ii], $triggerToken) !== false || $pluginTriggerTokens[$ii] === false)
2110  {
2111  // Apply the plugin; if the return value is TRUE continue to the next token
2112  if ($plugins[$ii]->apply($tokens[$i]) === true)
2113  {
2114  continue 2;
2115  }
2116  }
2117  }
2118  }
2119  }
2120  // Stringify the tokens
2121  for($i = 0; $i < $tokenCount; $i++)
2122  {
2123  $r .= (string) $tokens[$i];
2124  }
2125  $this->minified = $r;
2126  return $r;
2127  }
2128  }
2129 
2130 /**
2131  * CssMin - A (simple) css minifier with benefits
2132  *
2133  * --
2134  * Copyright (c) 2011 Joe Scylla <joe.scylla@gmail.com>
2135  *
2136  * Permission is hereby granted, free of charge, to any person obtaining a copy
2137  * of this software and associated documentation files (the "Software"), to deal
2138  * in the Software without restriction, including without limitation the rights
2139  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2140  * copies of the Software, and to permit persons to whom the Software is
2141  * furnished to do so, subject to the following conditions:
2142  *
2143  * The above copyright notice and this permission notice shall be included in
2144  * all copies or substantial portions of the Software.
2145  *
2146  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2147  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2148  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2149  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2150  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2151  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2152  * THE SOFTWARE.
2153  * --
2154  *
2155  * @package CssMin
2156  * @link http://code.google.com/p/cssmin/
2157  * @author Joe Scylla <joe.scylla@gmail.com>
2158  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
2159  * @license http://opensource.org/licenses/mit-license.php MIT License
2160  * @version 3.0.1
2161  */
2162 class CssMin
2163  {
2164  /**
2165  * Index of classes
2166  *
2167  * @var array
2168  */
2169  private static $classIndex = array();
2170  /**
2171  * Parse/minify errors
2172  *
2173  * @var array
2174  */
2175  private static $errors = array();
2176  /**
2177  * Verbose output.
2178  *
2179  * @var boolean
2180  */
2181  private static $isVerbose = false;
2182  /**
2183  * {@link http://goo.gl/JrW54 Autoload} function of CssMin.
2184  *
2185  * @param string $class Name of the class
2186  * @return void
2187  */
2188  public static function autoload($class)
2189  {
2190  if (isset(self::$classIndex[$class]))
2191  {
2192  require(self::$classIndex[$class]);
2193  }
2194  }
2195  /**
2196  * Return errors
2197  *
2198  * @return array of {CssError}.
2199  */
2200  public static function getErrors()
2201  {
2202  return self::$errors;
2203  }
2204  /**
2205  * Returns if there were errors.
2206  *
2207  * @return boolean
2208  */
2209  public static function hasErrors()
2210  {
2211  return count(self::$errors) > 0;
2212  }
2213  /**
2214  * Initialises CssMin.
2215  *
2216  * @return void
2217  */
2218  public static function initialise()
2219  {
2220  // Create the class index for autoloading or including
2221  $paths = array(dirname(__FILE__));
2222  while (list($i, $path) = each($paths))
2223  {
2224  $subDirectorys = glob($path . "*", GLOB_MARK | GLOB_ONLYDIR | GLOB_NOSORT);
2225  if (is_array($subDirectorys))
2226  {
2227  foreach ($subDirectorys as $subDirectory)
2228  {
2229  $paths[] = $subDirectory;
2230  }
2231  }
2232  $files = glob($path . "*.php", 0);
2233  if (is_array($files))
2234  {
2235  foreach ($files as $file)
2236  {
2237  $class = substr(basename($file), 0, -4);
2238  self::$classIndex[$class] = $file;
2239  }
2240  }
2241  }
2242  krsort(self::$classIndex);
2243  // Only use autoloading if spl_autoload_register() is available and no __autoload() is defined (because
2244  // __autoload() breaks if spl_autoload_register() is used.
2245  if (function_exists("spl_autoload_register") && !is_callable("__autoload"))
2246  {
2247  spl_autoload_register(array(__CLASS__, "autoload"));
2248  }
2249  // Otherwise include all class files
2250  else
2251  {
2252  foreach (self::$classIndex as $class => $file)
2253  {
2254  if (!class_exists($class))
2255  {
2256  require_once($file);
2257  }
2258  }
2259  }
2260  }
2261  /**
2262  * Minifies CSS source.
2263  *
2264  * @param string $source CSS source
2265  * @param array $filters Filter configuration [optional]
2266  * @param array $plugins Plugin configuration [optional]
2267  * @return string Minified CSS
2268  */
2269  public static function minify($source, array $filters = null, array $plugins = null)
2270  {
2271  self::$errors = array();
2272  $minifier = new CssMinifier($source, $filters, $plugins);
2273  return $minifier->getMinified();
2274  }
2275  /**
2276  * Parse the CSS source.
2277  *
2278  * @param string $source CSS source
2279  * @param array $plugins Plugin configuration [optional]
2280  * @return array Array of aCssToken
2281  */
2282  public static function parse($source, array $plugins = null)
2283  {
2284  self::$errors = array();
2285  $parser = new CssParser($source, $plugins);
2286  return $parser->getTokens();
2287  }
2288  /**
2289  * --
2290  *
2291  * @param boolean $to
2292  * @return boolean
2293  */
2294  public static function setVerbose($to)
2295  {
2296  self::$isVerbose = (boolean) $to;
2297  return self::$isVerbose;
2298  }
2299  /**
2300  * --
2301  *
2302  * @param CssError $error
2303  * @return void
2304  */
2305  public static function triggerError(CssError $error)
2306  {
2307  self::$errors[] = $error;
2308  if (self::$isVerbose)
2309  {
2310  trigger_error((string) $error, E_USER_WARNING);
2311  }
2312  }
2313  }
2314 // Initialises CssMin
2316 
2317 /**
2318  * This {@link aCssMinifierFilter minifier filter} import external css files defined with the @import at-rule into the
2319  * current stylesheet.
2320  *
2321  * @package CssMin/Minifier/Filters
2322  * @link http://code.google.com/p/cssmin/
2323  * @author Joe Scylla <joe.scylla@gmail.com>
2324  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
2325  * @license http://opensource.org/licenses/mit-license.php MIT License
2326  * @version 3.0.1
2327  */
2329  {
2330  /**
2331  * Array with already imported external stylesheets.
2332  *
2333  * @var array
2334  */
2335  private $imported = array();
2336  /**
2337  * Implements {@link aCssMinifierFilter::filter()}.
2338  *
2339  * @param array $tokens Array of objects of type aCssToken
2340  * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
2341  */
2342  public function apply(array &$tokens)
2343  {
2344  if (!isset($this->configuration["BasePath"]) || !is_dir($this->configuration["BasePath"]))
2345  {
2346  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Base path <code>" . ($this->configuration["BasePath"] ? $this->configuration["BasePath"] : "null"). "</code> is not a directory"));
2347  return 0;
2348  }
2349  for ($i = 0, $l = count($tokens); $i < $l; $i++)
2350  {
2351  if (get_class($tokens[$i]) === "CssAtImportToken")
2352  {
2353  $import = $this->configuration["BasePath"] . "/" . $tokens[$i]->Import;
2354  // Import file was not found/is not a file
2355  if (!is_file($import))
2356  {
2357  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Import file <code>" . $import. "</code> was not found.", (string) $tokens[$i]));
2358  }
2359  // Import file already imported; remove this @import at-rule to prevent recursions
2360  elseif (in_array($import, $this->imported))
2361  {
2362  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Import file <code>" . $import. "</code> was already imported.", (string) $tokens[$i]));
2363  $tokens[$i] = null;
2364  }
2365  else
2366  {
2367  $this->imported[] = $import;
2368  $parser = new CssParser(file_get_contents($import));
2369  $import = $parser->getTokens();
2370  // The @import at-rule has media types defined requiring special handling
2371  if (count($tokens[$i]->MediaTypes) > 0 && !(count($tokens[$i]->MediaTypes) == 1 && $tokens[$i]->MediaTypes[0] == "all"))
2372  {
2373  $blocks = array();
2374  /*
2375  * Filter or set media types of @import at-rule or remove the @import at-rule if no media type is matching the parent @import at-rule
2376  */
2377  for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
2378  {
2379  if (get_class($import[$ii]) === "CssAtImportToken")
2380  {
2381  // @import at-rule defines no media type or only the "all" media type; set the media types to the one defined in the parent @import at-rule
2382  if (count($import[$ii]->MediaTypes) == 0 || (count($import[$ii]->MediaTypes) == 1 && $import[$ii]->MediaTypes[0] == "all"))
2383  {
2384  $import[$ii]->MediaTypes = $tokens[$i]->MediaTypes;
2385  }
2386  // @import at-rule defineds one or more media types; filter out media types not matching with the parent @import at-rule
2387  elseif (count($import[$ii]->MediaTypes > 0))
2388  {
2389  foreach ($import[$ii]->MediaTypes as $index => $mediaType)
2390  {
2391  if (!in_array($mediaType, $tokens[$i]->MediaTypes))
2392  {
2393  unset($import[$ii]->MediaTypes[$index]);
2394  }
2395  }
2396  $import[$ii]->MediaTypes = array_values($import[$ii]->MediaTypes);
2397  // If there are no media types left in the @import at-rule remove the @import at-rule
2398  if (count($import[$ii]->MediaTypes) == 0)
2399  {
2400  $import[$ii] = null;
2401  }
2402  }
2403  }
2404  }
2405  /*
2406  * Remove media types of @media at-rule block not defined in the @import at-rule
2407  */
2408  for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
2409  {
2410  if (get_class($import[$ii]) === "CssAtMediaStartToken")
2411  {
2412  foreach ($import[$ii]->MediaTypes as $index => $mediaType)
2413  {
2414  if (!in_array($mediaType, $tokens[$i]->MediaTypes))
2415  {
2416  unset($import[$ii]->MediaTypes[$index]);
2417  }
2418  $import[$ii]->MediaTypes = array_values($import[$ii]->MediaTypes);
2419  }
2420  }
2421  }
2422  /*
2423  * If no media types left of the @media at-rule block remove the complete block
2424  */
2425  for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
2426  {
2427  if (get_class($import[$ii]) === "CssAtMediaStartToken")
2428  {
2429  if (count($import[$ii]->MediaTypes) === 0)
2430  {
2431  for ($iii = $ii; $iii < $ll; $iii++)
2432  {
2433  if (get_class($import[$iii]) === "CssAtMediaEndToken")
2434  {
2435  break;
2436  }
2437  }
2438  if (get_class($import[$iii]) === "CssAtMediaEndToken")
2439  {
2440  array_splice($import, $ii, $iii - $ii + 1, array());
2441  $ll = count($import);
2442  }
2443  }
2444  }
2445  }
2446  /*
2447  * If the media types of the @media at-rule equals the media types defined in the @import
2448  * at-rule remove the CssAtMediaStartToken and CssAtMediaEndToken token
2449  */
2450  for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
2451  {
2452  if (get_class($import[$ii]) === "CssAtMediaStartToken" && count(array_diff($tokens[$i]->MediaTypes, $import[$ii]->MediaTypes)) === 0)
2453  {
2454  for ($iii = $ii; $iii < $ll; $iii++)
2455  {
2456  if (get_class($import[$iii]) == "CssAtMediaEndToken")
2457  {
2458  break;
2459  }
2460  }
2461  if (get_class($import[$iii]) == "CssAtMediaEndToken")
2462  {
2463  unset($import[$ii]);
2464  unset($import[$iii]);
2465  $import = array_values($import);
2466  $ll = count($import);
2467  }
2468  }
2469  }
2470  /**
2471  * Extract CssAtImportToken and CssAtCharsetToken tokens
2472  */
2473  for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
2474  {
2475  $class = get_class($import[$ii]);
2476  if ($class === "CssAtImportToken" || $class === "CssAtCharsetToken")
2477  {
2478  $blocks = array_merge($blocks, array_splice($import, $ii, 1, array()));
2479  $ll = count($import);
2480  }
2481  }
2482  /*
2483  * Extract the @font-face, @media and @page at-rule block
2484  */
2485  for($ii = 0, $ll = count($import); $ii < $ll; $ii++)
2486  {
2487  $class = get_class($import[$ii]);
2488  if ($class === "CssAtFontFaceStartToken" || $class === "CssAtMediaStartToken" || $class === "CssAtPageStartToken" || $class === "CssAtVariablesStartToken")
2489  {
2490  for ($iii = $ii; $iii < $ll; $iii++)
2491  {
2492  $class = get_class($import[$iii]);
2493  if ($class === "CssAtFontFaceEndToken" || $class === "CssAtMediaEndToken" || $class === "CssAtPageEndToken" || $class === "CssAtVariablesEndToken")
2494  {
2495  break;
2496  }
2497  }
2498  $class = get_class($import[$iii]);
2499  if (isset($import[$iii]) && ($class === "CssAtFontFaceEndToken" || $class === "CssAtMediaEndToken" || $class === "CssAtPageEndToken" || $class === "CssAtVariablesEndToken"))
2500  {
2501  $blocks = array_merge($blocks, array_splice($import, $ii, $iii - $ii + 1, array()));
2502  $ll = count($import);
2503  }
2504  }
2505  }
2506  // Create the import array with extracted tokens and the rulesets wrapped into a @media at-rule block
2507  $import = array_merge($blocks, array(new CssAtMediaStartToken($tokens[$i]->MediaTypes)), $import, array(new CssAtMediaEndToken()));
2508  }
2509  // Insert the imported tokens
2510  array_splice($tokens, $i, 1, $import);
2511  // Modify parameters of the for-loop
2512  $i--;
2513  $l = count($tokens);
2514  }
2515  }
2516  }
2517  }
2518  }
2519 
2520 /**
2521  * {@link aCssParserPlugin Parser plugin} for preserve parsing expression() declaration values.
2522  *
2523  * This plugin return no {@link aCssToken CssToken} but ensures that expression() declaration values will get parsed
2524  * properly.
2525  *
2526  * @package CssMin/Parser/Plugins
2527  * @link http://code.google.com/p/cssmin/
2528  * @author Joe Scylla <joe.scylla@gmail.com>
2529  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
2530  * @license http://opensource.org/licenses/mit-license.php MIT License
2531  * @version 3.0.1
2532  */
2534  {
2535  /**
2536  * Count of left braces.
2537  *
2538  * @var integer
2539  */
2540  private $leftBraces = 0;
2541  /**
2542  * Count of right braces.
2543  *
2544  * @var integer
2545  */
2546  private $rightBraces = 0;
2547  /**
2548  * Implements {@link aCssParserPlugin::getTriggerChars()}.
2549  *
2550  * @return array
2551  */
2552  public function getTriggerChars()
2553  {
2554  return array("(", ")", ";", "}");
2555  }
2556  /**
2557  * Implements {@link aCssParserPlugin::getTriggerStates()}.
2558  *
2559  * @return array
2560  */
2561  public function getTriggerStates()
2562  {
2563  return false;
2564  }
2565  /**
2566  * Implements {@link aCssParserPlugin::parse()}.
2567  *
2568  * @param integer $index Current index
2569  * @param string $char Current char
2570  * @param string $previousChar Previous char
2571  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
2572  */
2573  public function parse($index, $char, $previousChar, $state)
2574  {
2575  // Start of expression
2576  if ($char === "(" && strtolower(substr($this->parser->getSource(), $index - 10, 11)) === "expression(" && $state !== "T_EXPRESSION")
2577  {
2578  $this->parser->pushState("T_EXPRESSION");
2579  $this->leftBraces++;
2580  }
2581  // Count left braces
2582  elseif ($char === "(" && $state === "T_EXPRESSION")
2583  {
2584  $this->leftBraces++;
2585  }
2586  // Count right braces
2587  elseif ($char === ")" && $state === "T_EXPRESSION")
2588  {
2589  $this->rightBraces++;
2590  }
2591  // Possible end of expression; if left and right braces are equal the expressen ends
2592  elseif (($char === ";" || $char === "}") && $state === "T_EXPRESSION" && $this->leftBraces === $this->rightBraces)
2593  {
2594  $this->leftBraces = $this->rightBraces = 0;
2595  $this->parser->popState();
2596  return $index - 1;
2597  }
2598  else
2599  {
2600  return false;
2601  }
2602  return true;
2603  }
2604  }
2605 
2606 /**
2607  * CSS Error.
2608  *
2609  * @package CssMin
2610  * @link http://code.google.com/p/cssmin/
2611  * @author Joe Scylla <joe.scylla@gmail.com>
2612  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
2613  * @license http://opensource.org/licenses/mit-license.php MIT License
2614  * @version 3.0.1
2615  */
2617  {
2618  /**
2619  * File.
2620  *
2621  * @var string
2622  */
2623  public $File = "";
2624  /**
2625  * Line.
2626  *
2627  * @var integer
2628  */
2629  public $Line = 0;
2630  /**
2631  * Error message.
2632  *
2633  * @var string
2634  */
2635  public $Message = "";
2636  /**
2637  * Source.
2638  *
2639  * @var string
2640  */
2641  public $Source = "";
2642  /**
2643  * Constructor triggering the error.
2644  *
2645  * @param string $message Error message
2646  * @param string $source Corresponding line [optional]
2647  * @return void
2648  */
2649  public function __construct($file, $line, $message, $source = "")
2650  {
2651  $this->File = $file;
2652  $this->Line = $line;
2653  $this->Message = $message;
2654  $this->Source = $source;
2655  }
2656  /**
2657  * Returns the error as formatted string.
2658  *
2659  * @return string
2660  */
2661  public function __toString()
2662  {
2663  return $this->Message . ($this->Source ? ": <br /><code>" . $this->Source . "</code>": "") . "<br />in file " . $this->File . " at line " . $this->Line;
2664  }
2665  }
2666 
2667 /**
2668  * This {@link aCssMinifierPlugin} will convert a color value in rgb notation to hexadecimal notation.
2669  *
2670  * Example:
2671  * <code>
2672  * color: rgb(200,60%,5);
2673  * </code>
2674  *
2675  * Will get converted to:
2676  * <code>
2677  * color:#c89905;
2678  * </code>
2679  *
2680  * @package CssMin/Minifier/Plugins
2681  * @link http://code.google.com/p/cssmin/
2682  * @author Joe Scylla <joe.scylla@gmail.com>
2683  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
2684  * @license http://opensource.org/licenses/mit-license.php MIT License
2685  * @version 3.0.1
2686  */
2688  {
2689  /**
2690  * Regular expression matching the value.
2691  *
2692  * @var string
2693  */
2694  private $reMatch = "/rgb\s*\(\s*([0-9%]+)\s*,\s*([0-9%]+)\s*,\s*([0-9%]+)\s*\)/iS";
2695  /**
2696  * Implements {@link aCssMinifierPlugin::minify()}.
2697  *
2698  * @param aCssToken $token Token to process
2699  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
2700  */
2701  public function apply(aCssToken &$token)
2702  {
2703  if (stripos($token->Value, "rgb") !== false && preg_match($this->reMatch, $token->Value, $m))
2704  {
2705  for ($i = 1, $l = count($m); $i < $l; $i++)
2706  {
2707  if (strpos("%", $m[$i]) !== false)
2708  {
2709  $m[$i] = substr($m[$i], 0, -1);
2710  $m[$i] = (int) (256 * ($m[$i] / 100));
2711  }
2712  $m[$i] = str_pad(dechex($m[$i]), 2, "0", STR_PAD_LEFT);
2713  }
2714  $token->Value = str_replace($m[0], "#" . $m[1] . $m[2] . $m[3], $token->Value);
2715  }
2716  return false;
2717  }
2718  /**
2719  * Implements {@link aMinifierPlugin::getTriggerTokens()}
2720  *
2721  * @return array
2722  */
2723  public function getTriggerTokens()
2724  {
2725  return array
2726  (
2727  "CssAtFontFaceDeclarationToken",
2728  "CssAtPageDeclarationToken",
2729  "CssRulesetDeclarationToken"
2730  );
2731  }
2732  }
2733 
2734 /**
2735  * This {@link aCssMinifierPlugin} will convert named color values to hexadecimal notation.
2736  *
2737  * Example:
2738  * <code>
2739  * color: black;
2740  * border: 1px solid indigo;
2741  * </code>
2742  *
2743  * Will get converted to:
2744  * <code>
2745  * color:#000;
2746  * border:1px solid #4b0082;
2747  * </code>
2748  *
2749  * @package CssMin/Minifier/Plugins
2750  * @link http://code.google.com/p/cssmin/
2751  * @author Joe Scylla <joe.scylla@gmail.com>
2752  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
2753  * @license http://opensource.org/licenses/mit-license.php MIT License
2754  * @version 3.0.1
2755  */
2757  {
2758 
2759  /**
2760  * Regular expression matching the value.
2761  *
2762  * @var string
2763  */
2764  private $reMatch = null;
2765  /**
2766  * Regular expression replacing the value.
2767  *
2768  * @var string
2769  */
2770  private $reReplace = "\"\${1}\" . \$this->transformation[strtolower(\"\${2}\")] . \"\${3}\"";
2771  /**
2772  * Transformation table used by the {@link CssConvertNamedColorsMinifierPlugin::$reReplace replace regular expression}.
2773  *
2774  * @var array
2775  */
2776  private $transformation = array
2777  (
2778  "aliceblue" => "#f0f8ff",
2779  "antiquewhite" => "#faebd7",
2780  "aqua" => "#0ff",
2781  "aquamarine" => "#7fffd4",
2782  "azure" => "#f0ffff",
2783  "beige" => "#f5f5dc",
2784  "black" => "#000",
2785  "blue" => "#00f",
2786  "blueviolet" => "#8a2be2",
2787  "brown" => "#a52a2a",
2788  "burlywood" => "#deb887",
2789  "cadetblue" => "#5f9ea0",
2790  "chartreuse" => "#7fff00",
2791  "chocolate" => "#d2691e",
2792  "coral" => "#ff7f50",
2793  "cornflowerblue" => "#6495ed",
2794  "cornsilk" => "#fff8dc",
2795  "crimson" => "#dc143c",
2796  "darkblue" => "#00008b",
2797  "darkcyan" => "#008b8b",
2798  "darkgoldenrod" => "#b8860b",
2799  "darkgray" => "#a9a9a9",
2800  "darkgreen" => "#006400",
2801  "darkkhaki" => "#bdb76b",
2802  "darkmagenta" => "#8b008b",
2803  "darkolivegreen" => "#556b2f",
2804  "darkorange" => "#ff8c00",
2805  "darkorchid" => "#9932cc",
2806  "darkred" => "#8b0000",
2807  "darksalmon" => "#e9967a",
2808  "darkseagreen" => "#8fbc8f",
2809  "darkslateblue" => "#483d8b",
2810  "darkslategray" => "#2f4f4f",
2811  "darkturquoise" => "#00ced1",
2812  "darkviolet" => "#9400d3",
2813  "deeppink" => "#ff1493",
2814  "deepskyblue" => "#00bfff",
2815  "dimgray" => "#696969",
2816  "dodgerblue" => "#1e90ff",
2817  "firebrick" => "#b22222",
2818  "floralwhite" => "#fffaf0",
2819  "forestgreen" => "#228b22",
2820  "fuchsia" => "#f0f",
2821  "gainsboro" => "#dcdcdc",
2822  "ghostwhite" => "#f8f8ff",
2823  "gold" => "#ffd700",
2824  "goldenrod" => "#daa520",
2825  "gray" => "#808080",
2826  "green" => "#008000",
2827  "greenyellow" => "#adff2f",
2828  "honeydew" => "#f0fff0",
2829  "hotpink" => "#ff69b4",
2830  "indianred" => "#cd5c5c",
2831  "indigo" => "#4b0082",
2832  "ivory" => "#fffff0",
2833  "khaki" => "#f0e68c",
2834  "lavender" => "#e6e6fa",
2835  "lavenderblush" => "#fff0f5",
2836  "lawngreen" => "#7cfc00",
2837  "lemonchiffon" => "#fffacd",
2838  "lightblue" => "#add8e6",
2839  "lightcoral" => "#f08080",
2840  "lightcyan" => "#e0ffff",
2841  "lightgoldenrodyellow" => "#fafad2",
2842  "lightgreen" => "#90ee90",
2843  "lightgrey" => "#d3d3d3",
2844  "lightpink" => "#ffb6c1",
2845  "lightsalmon" => "#ffa07a",
2846  "lightseagreen" => "#20b2aa",
2847  "lightskyblue" => "#87cefa",
2848  "lightslategray" => "#789",
2849  "lightsteelblue" => "#b0c4de",
2850  "lightyellow" => "#ffffe0",
2851  "lime" => "#0f0",
2852  "limegreen" => "#32cd32",
2853  "linen" => "#faf0e6",
2854  "maroon" => "#800000",
2855  "mediumaquamarine" => "#66cdaa",
2856  "mediumblue" => "#0000cd",
2857  "mediumorchid" => "#ba55d3",
2858  "mediumpurple" => "#9370db",
2859  "mediumseagreen" => "#3cb371",
2860  "mediumslateblue" => "#7b68ee",
2861  "mediumspringgreen" => "#00fa9a",
2862  "mediumturquoise" => "#48d1cc",
2863  "mediumvioletred" => "#c71585",
2864  "midnightblue" => "#191970",
2865  "mintcream" => "#f5fffa",
2866  "mistyrose" => "#ffe4e1",
2867  "moccasin" => "#ffe4b5",
2868  "navajowhite" => "#ffdead",
2869  "navy" => "#000080",
2870  "oldlace" => "#fdf5e6",
2871  "olive" => "#808000",
2872  "olivedrab" => "#6b8e23",
2873  "orange" => "#ffa500",
2874  "orangered" => "#ff4500",
2875  "orchid" => "#da70d6",
2876  "palegoldenrod" => "#eee8aa",
2877  "palegreen" => "#98fb98",
2878  "paleturquoise" => "#afeeee",
2879  "palevioletred" => "#db7093",
2880  "papayawhip" => "#ffefd5",
2881  "peachpuff" => "#ffdab9",
2882  "peru" => "#cd853f",
2883  "pink" => "#ffc0cb",
2884  "plum" => "#dda0dd",
2885  "powderblue" => "#b0e0e6",
2886  "purple" => "#800080",
2887  "red" => "#f00",
2888  "rosybrown" => "#bc8f8f",
2889  "royalblue" => "#4169e1",
2890  "saddlebrown" => "#8b4513",
2891  "salmon" => "#fa8072",
2892  "sandybrown" => "#f4a460",
2893  "seagreen" => "#2e8b57",
2894  "seashell" => "#fff5ee",
2895  "sienna" => "#a0522d",
2896  "silver" => "#c0c0c0",
2897  "skyblue" => "#87ceeb",
2898  "slateblue" => "#6a5acd",
2899  "slategray" => "#708090",
2900  "snow" => "#fffafa",
2901  "springgreen" => "#00ff7f",
2902  "steelblue" => "#4682b4",
2903  "tan" => "#d2b48c",
2904  "teal" => "#008080",
2905  "thistle" => "#d8bfd8",
2906  "tomato" => "#ff6347",
2907  "turquoise" => "#40e0d0",
2908  "violet" => "#ee82ee",
2909  "wheat" => "#f5deb3",
2910  "white" => "#fff",
2911  "whitesmoke" => "#f5f5f5",
2912  "yellow" => "#ff0",
2913  "yellowgreen" => "#9acd32"
2914  );
2915  /**
2916  * Overwrites {@link aCssMinifierPlugin::__construct()}.
2917  *
2918  * The constructor will create the {@link CssConvertNamedColorsMinifierPlugin::$reReplace replace regular expression}
2919  * based on the {@link CssConvertNamedColorsMinifierPlugin::$transformation transformation table}.
2920  *
2921  * @param CssMinifier $minifier The CssMinifier object of this plugin.
2922  * @param array $configuration Plugin configuration [optional]
2923  * @return void
2924  */
2925  public function __construct(CssMinifier $minifier, array $configuration = array())
2926  {
2927  $this->reMatch = "/(^|\s)+(" . implode("|", array_keys($this->transformation)) . ")(\s|$)+/eiS";
2928  parent::__construct($minifier, $configuration);
2929  }
2930  /**
2931  * Implements {@link aCssMinifierPlugin::minify()}.
2932  *
2933  * @param aCssToken $token Token to process
2934  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
2935  */
2936  public function apply(aCssToken &$token)
2937  {
2938  $lcValue = strtolower($token->Value);
2939  // Declaration value equals a value in the transformation table => simple replace
2940  if (isset($this->transformation[$lcValue]))
2941  {
2942  $token->Value = $this->transformation[$lcValue];
2943  }
2944  // Declaration value contains a value in the transformation table => regular expression replace
2945  elseif (preg_match($this->reMatch, $token->Value))
2946  {
2947  $token->Value = preg_replace($this->reMatch, $this->reReplace, $token->Value);
2948  }
2949  return false;
2950  }
2951  /**
2952  * Implements {@link aMinifierPlugin::getTriggerTokens()}
2953  *
2954  * @return array
2955  */
2956  public function getTriggerTokens()
2957  {
2958  return array
2959  (
2960  "CssAtFontFaceDeclarationToken",
2961  "CssAtPageDeclarationToken",
2962  "CssRulesetDeclarationToken"
2963  );
2964  }
2965  }
2966 
2967 /**
2968  * This {@link aCssMinifierFilter minifier filter} triggers on CSS Level 3 properties and will add declaration tokens
2969  * with browser-specific properties.
2970  *
2971  * @package CssMin/Minifier/Filters
2972  * @link http://code.google.com/p/cssmin/
2973  * @author Joe Scylla <joe.scylla@gmail.com>
2974  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
2975  * @license http://opensource.org/licenses/mit-license.php MIT License
2976  * @version 3.0.1
2977  */
2979  {
2980  /**
2981  * Css property transformations table. Used to convert CSS3 and proprietary properties to the browser-specific
2982  * counterparts.
2983  *
2984  * @var array
2985  */
2986  private $transformations = array
2987  (
2988  // Property Array(Mozilla, Webkit, Opera, Internet Explorer); NULL values are placeholders and will get ignored
2989  "animation" => array(null, "-webkit-animation", null, null),
2990  "animation-delay" => array(null, "-webkit-animation-delay", null, null),
2991  "animation-direction" => array(null, "-webkit-animation-direction", null, null),
2992  "animation-duration" => array(null, "-webkit-animation-duration", null, null),
2993  "animation-fill-mode" => array(null, "-webkit-animation-fill-mode", null, null),
2994  "animation-iteration-count" => array(null, "-webkit-animation-iteration-count", null, null),
2995  "animation-name" => array(null, "-webkit-animation-name", null, null),
2996  "animation-play-state" => array(null, "-webkit-animation-play-state", null, null),
2997  "animation-timing-function" => array(null, "-webkit-animation-timing-function", null, null),
2998  "appearance" => array("-moz-appearance", "-webkit-appearance", null, null),
2999  "backface-visibility" => array(null, "-webkit-backface-visibility", null, null),
3000  "background-clip" => array(null, "-webkit-background-clip", null, null),
3001  "background-composite" => array(null, "-webkit-background-composite", null, null),
3002  "background-inline-policy" => array("-moz-background-inline-policy", null, null, null),
3003  "background-origin" => array(null, "-webkit-background-origin", null, null),
3004  "background-position-x" => array(null, null, null, "-ms-background-position-x"),
3005  "background-position-y" => array(null, null, null, "-ms-background-position-y"),
3006  "background-size" => array(null, "-webkit-background-size", null, null),
3007  "behavior" => array(null, null, null, "-ms-behavior"),
3008  "binding" => array("-moz-binding", null, null, null),
3009  "border-after" => array(null, "-webkit-border-after", null, null),
3010  "border-after-color" => array(null, "-webkit-border-after-color", null, null),
3011  "border-after-style" => array(null, "-webkit-border-after-style", null, null),
3012  "border-after-width" => array(null, "-webkit-border-after-width", null, null),
3013  "border-before" => array(null, "-webkit-border-before", null, null),
3014  "border-before-color" => array(null, "-webkit-border-before-color", null, null),
3015  "border-before-style" => array(null, "-webkit-border-before-style", null, null),
3016  "border-before-width" => array(null, "-webkit-border-before-width", null, null),
3017  "border-border-bottom-colors" => array("-moz-border-bottom-colors", null, null, null),
3018  "border-bottom-left-radius" => array("-moz-border-radius-bottomleft", "-webkit-border-bottom-left-radius", null, null),
3019  "border-bottom-right-radius" => array("-moz-border-radius-bottomright", "-webkit-border-bottom-right-radius", null, null),
3020  "border-end" => array("-moz-border-end", "-webkit-border-end", null, null),
3021  "border-end-color" => array("-moz-border-end-color", "-webkit-border-end-color", null, null),
3022  "border-end-style" => array("-moz-border-end-style", "-webkit-border-end-style", null, null),
3023  "border-end-width" => array("-moz-border-end-width", "-webkit-border-end-width", null, null),
3024  "border-fit" => array(null, "-webkit-border-fit", null, null),
3025  "border-horizontal-spacing" => array(null, "-webkit-border-horizontal-spacing", null, null),
3026  "border-image" => array("-moz-border-image", "-webkit-border-image", null, null),
3027  "border-left-colors" => array("-moz-border-left-colors", null, null, null),
3028  "border-radius" => array("-moz-border-radius", "-webkit-border-radius", null, null),
3029  "border-border-right-colors" => array("-moz-border-right-colors", null, null, null),
3030  "border-start" => array("-moz-border-start", "-webkit-border-start", null, null),
3031  "border-start-color" => array("-moz-border-start-color", "-webkit-border-start-color", null, null),
3032  "border-start-style" => array("-moz-border-start-style", "-webkit-border-start-style", null, null),
3033  "border-start-width" => array("-moz-border-start-width", "-webkit-border-start-width", null, null),
3034  "border-top-colors" => array("-moz-border-top-colors", null, null, null),
3035  "border-top-left-radius" => array("-moz-border-radius-topleft", "-webkit-border-top-left-radius", null, null),
3036  "border-top-right-radius" => array("-moz-border-radius-topright", "-webkit-border-top-right-radius", null, null),
3037  "border-vertical-spacing" => array(null, "-webkit-border-vertical-spacing", null, null),
3038  "box-align" => array("-moz-box-align", "-webkit-box-align", null, null),
3039  "box-direction" => array("-moz-box-direction", "-webkit-box-direction", null, null),
3040  "box-flex" => array("-moz-box-flex", "-webkit-box-flex", null, null),
3041  "box-flex-group" => array(null, "-webkit-box-flex-group", null, null),
3042  "box-flex-lines" => array(null, "-webkit-box-flex-lines", null, null),
3043  "box-ordinal-group" => array("-moz-box-ordinal-group", "-webkit-box-ordinal-group", null, null),
3044  "box-orient" => array("-moz-box-orient", "-webkit-box-orient", null, null),
3045  "box-pack" => array("-moz-box-pack", "-webkit-box-pack", null, null),
3046  "box-reflect" => array(null, "-webkit-box-reflect", null, null),
3047  "box-shadow" => array("-moz-box-shadow", "-webkit-box-shadow", null, null),
3048  "box-sizing" => array("-moz-box-sizing", null, null, null),
3049  "color-correction" => array(null, "-webkit-color-correction", null, null),
3050  "column-break-after" => array(null, "-webkit-column-break-after", null, null),
3051  "column-break-before" => array(null, "-webkit-column-break-before", null, null),
3052  "column-break-inside" => array(null, "-webkit-column-break-inside", null, null),
3053  "column-count" => array("-moz-column-count", "-webkit-column-count", null, null),
3054  "column-gap" => array("-moz-column-gap", "-webkit-column-gap", null, null),
3055  "column-rule" => array("-moz-column-rule", "-webkit-column-rule", null, null),
3056  "column-rule-color" => array("-moz-column-rule-color", "-webkit-column-rule-color", null, null),
3057  "column-rule-style" => array("-moz-column-rule-style", "-webkit-column-rule-style", null, null),
3058  "column-rule-width" => array("-moz-column-rule-width", "-webkit-column-rule-width", null, null),
3059  "column-span" => array(null, "-webkit-column-span", null, null),
3060  "column-width" => array("-moz-column-width", "-webkit-column-width", null, null),
3061  "columns" => array(null, "-webkit-columns", null, null),
3062  "filter" => array(__CLASS__, "filter"),
3063  "float-edge" => array("-moz-float-edge", null, null, null),
3064  "font-feature-settings" => array("-moz-font-feature-settings", null, null, null),
3065  "font-language-override" => array("-moz-font-language-override", null, null, null),
3066  "font-size-delta" => array(null, "-webkit-font-size-delta", null, null),
3067  "font-smoothing" => array(null, "-webkit-font-smoothing", null, null),
3068  "force-broken-image-icon" => array("-moz-force-broken-image-icon", null, null, null),
3069  "highlight" => array(null, "-webkit-highlight", null, null),
3070  "hyphenate-character" => array(null, "-webkit-hyphenate-character", null, null),
3071  "hyphenate-locale" => array(null, "-webkit-hyphenate-locale", null, null),
3072  "hyphens" => array(null, "-webkit-hyphens", null, null),
3073  "force-broken-image-icon" => array("-moz-image-region", null, null, null),
3074  "ime-mode" => array(null, null, null, "-ms-ime-mode"),
3075  "interpolation-mode" => array(null, null, null, "-ms-interpolation-mode"),
3076  "layout-flow" => array(null, null, null, "-ms-layout-flow"),
3077  "layout-grid" => array(null, null, null, "-ms-layout-grid"),
3078  "layout-grid-char" => array(null, null, null, "-ms-layout-grid-char"),
3079  "layout-grid-line" => array(null, null, null, "-ms-layout-grid-line"),
3080  "layout-grid-mode" => array(null, null, null, "-ms-layout-grid-mode"),
3081  "layout-grid-type" => array(null, null, null, "-ms-layout-grid-type"),
3082  "line-break" => array(null, "-webkit-line-break", null, "-ms-line-break"),
3083  "line-clamp" => array(null, "-webkit-line-clamp", null, null),
3084  "line-grid-mode" => array(null, null, null, "-ms-line-grid-mode"),
3085  "logical-height" => array(null, "-webkit-logical-height", null, null),
3086  "logical-width" => array(null, "-webkit-logical-width", null, null),
3087  "margin-after" => array(null, "-webkit-margin-after", null, null),
3088  "margin-after-collapse" => array(null, "-webkit-margin-after-collapse", null, null),
3089  "margin-before" => array(null, "-webkit-margin-before", null, null),
3090  "margin-before-collapse" => array(null, "-webkit-margin-before-collapse", null, null),
3091  "margin-bottom-collapse" => array(null, "-webkit-margin-bottom-collapse", null, null),
3092  "margin-collapse" => array(null, "-webkit-margin-collapse", null, null),
3093  "margin-end" => array("-moz-margin-end", "-webkit-margin-end", null, null),
3094  "margin-start" => array("-moz-margin-start", "-webkit-margin-start", null, null),
3095  "margin-top-collapse" => array(null, "-webkit-margin-top-collapse", null, null),
3096  "marquee " => array(null, "-webkit-marquee", null, null),
3097  "marquee-direction" => array(null, "-webkit-marquee-direction", null, null),
3098  "marquee-increment" => array(null, "-webkit-marquee-increment", null, null),
3099  "marquee-repetition" => array(null, "-webkit-marquee-repetition", null, null),
3100  "marquee-speed" => array(null, "-webkit-marquee-speed", null, null),
3101  "marquee-style" => array(null, "-webkit-marquee-style", null, null),
3102  "mask" => array(null, "-webkit-mask", null, null),
3103  "mask-attachment" => array(null, "-webkit-mask-attachment", null, null),
3104  "mask-box-image" => array(null, "-webkit-mask-box-image", null, null),
3105  "mask-clip" => array(null, "-webkit-mask-clip", null, null),
3106  "mask-composite" => array(null, "-webkit-mask-composite", null, null),
3107  "mask-image" => array(null, "-webkit-mask-image", null, null),
3108  "mask-origin" => array(null, "-webkit-mask-origin", null, null),
3109  "mask-position" => array(null, "-webkit-mask-position", null, null),
3110  "mask-position-x" => array(null, "-webkit-mask-position-x", null, null),
3111  "mask-position-y" => array(null, "-webkit-mask-position-y", null, null),
3112  "mask-repeat" => array(null, "-webkit-mask-repeat", null, null),
3113  "mask-repeat-x" => array(null, "-webkit-mask-repeat-x", null, null),
3114  "mask-repeat-y" => array(null, "-webkit-mask-repeat-y", null, null),
3115  "mask-size" => array(null, "-webkit-mask-size", null, null),
3116  "match-nearest-mail-blockquote-color" => array(null, "-webkit-match-nearest-mail-blockquote-color", null, null),
3117  "max-logical-height" => array(null, "-webkit-max-logical-height", null, null),
3118  "max-logical-width" => array(null, "-webkit-max-logical-width", null, null),
3119  "min-logical-height" => array(null, "-webkit-min-logical-height", null, null),
3120  "min-logical-width" => array(null, "-webkit-min-logical-width", null, null),
3121  "object-fit" => array(null, null, "-o-object-fit", null),
3122  "object-position" => array(null, null, "-o-object-position", null),
3123  "opacity" => array(__CLASS__, "opacity"),
3124  "outline-radius" => array("-moz-outline-radius", null, null, null),
3125  "outline-bottom-left-radius" => array("-moz-outline-radius-bottomleft", null, null, null),
3126  "outline-bottom-right-radius" => array("-moz-outline-radius-bottomright", null, null, null),
3127  "outline-top-left-radius" => array("-moz-outline-radius-topleft", null, null, null),
3128  "outline-top-right-radius" => array("-moz-outline-radius-topright", null, null, null),
3129  "padding-after" => array(null, "-webkit-padding-after", null, null),
3130  "padding-before" => array(null, "-webkit-padding-before", null, null),
3131  "padding-end" => array("-moz-padding-end", "-webkit-padding-end", null, null),
3132  "padding-start" => array("-moz-padding-start", "-webkit-padding-start", null, null),
3133  "perspective" => array(null, "-webkit-perspective", null, null),
3134  "perspective-origin" => array(null, "-webkit-perspective-origin", null, null),
3135  "perspective-origin-x" => array(null, "-webkit-perspective-origin-x", null, null),
3136  "perspective-origin-y" => array(null, "-webkit-perspective-origin-y", null, null),
3137  "rtl-ordering" => array(null, "-webkit-rtl-ordering", null, null),
3138  "scrollbar-3dlight-color" => array(null, null, null, "-ms-scrollbar-3dlight-color"),
3139  "scrollbar-arrow-color" => array(null, null, null, "-ms-scrollbar-arrow-color"),
3140  "scrollbar-base-color" => array(null, null, null, "-ms-scrollbar-base-color"),
3141  "scrollbar-darkshadow-color" => array(null, null, null, "-ms-scrollbar-darkshadow-color"),
3142  "scrollbar-face-color" => array(null, null, null, "-ms-scrollbar-face-color"),
3143  "scrollbar-highlight-color" => array(null, null, null, "-ms-scrollbar-highlight-color"),
3144  "scrollbar-shadow-color" => array(null, null, null, "-ms-scrollbar-shadow-color"),
3145  "scrollbar-track-color" => array(null, null, null, "-ms-scrollbar-track-color"),
3146  "stack-sizing" => array("-moz-stack-sizing", null, null, null),
3147  "svg-shadow" => array(null, "-webkit-svg-shadow", null, null),
3148  "tab-size" => array("-moz-tab-size", null, "-o-tab-size", null),
3149  "table-baseline" => array(null, null, "-o-table-baseline", null),
3150  "text-align-last" => array(null, null, null, "-ms-text-align-last"),
3151  "text-autospace" => array(null, null, null, "-ms-text-autospace"),
3152  "text-combine" => array(null, "-webkit-text-combine", null, null),
3153  "text-decorations-in-effect" => array(null, "-webkit-text-decorations-in-effect", null, null),
3154  "text-emphasis" => array(null, "-webkit-text-emphasis", null, null),
3155  "text-emphasis-color" => array(null, "-webkit-text-emphasis-color", null, null),
3156  "text-emphasis-position" => array(null, "-webkit-text-emphasis-position", null, null),
3157  "text-emphasis-style" => array(null, "-webkit-text-emphasis-style", null, null),
3158  "text-fill-color" => array(null, "-webkit-text-fill-color", null, null),
3159  "text-justify" => array(null, null, null, "-ms-text-justify"),
3160  "text-kashida-space" => array(null, null, null, "-ms-text-kashida-space"),
3161  "text-overflow" => array(null, null, "-o-text-overflow", "-ms-text-overflow"),
3162  "text-security" => array(null, "-webkit-text-security", null, null),
3163  "text-size-adjust" => array(null, "-webkit-text-size-adjust", null, "-ms-text-size-adjust"),
3164  "text-stroke" => array(null, "-webkit-text-stroke", null, null),
3165  "text-stroke-color" => array(null, "-webkit-text-stroke-color", null, null),
3166  "text-stroke-width" => array(null, "-webkit-text-stroke-width", null, null),
3167  "text-underline-position" => array(null, null, null, "-ms-text-underline-position"),
3168  "transform" => array("-moz-transform", "-webkit-transform", "-o-transform", null),
3169  "transform-origin" => array("-moz-transform-origin", "-webkit-transform-origin", "-o-transform-origin", null),
3170  "transform-origin-x" => array(null, "-webkit-transform-origin-x", null, null),
3171  "transform-origin-y" => array(null, "-webkit-transform-origin-y", null, null),
3172  "transform-origin-z" => array(null, "-webkit-transform-origin-z", null, null),
3173  "transform-style" => array(null, "-webkit-transform-style", null, null),
3174  "transition" => array("-moz-transition", "-webkit-transition", "-o-transition", null),
3175  "transition-delay" => array("-moz-transition-delay", "-webkit-transition-delay", "-o-transition-delay", null),
3176  "transition-duration" => array("-moz-transition-duration", "-webkit-transition-duration", "-o-transition-duration", null),
3177  "transition-property" => array("-moz-transition-property", "-webkit-transition-property", "-o-transition-property", null),
3178  "transition-timing-function" => array("-moz-transition-timing-function", "-webkit-transition-timing-function", "-o-transition-timing-function", null),
3179  "user-drag" => array(null, "-webkit-user-drag", null, null),
3180  "user-focus" => array("-moz-user-focus", null, null, null),
3181  "user-input" => array("-moz-user-input", null, null, null),
3182  "user-modify" => array("-moz-user-modify", "-webkit-user-modify", null, null),
3183  "user-select" => array("-moz-user-select", "-webkit-user-select", null, null),
3184  "white-space" => array(__CLASS__, "whiteSpace"),
3185  "window-shadow" => array("-moz-window-shadow", null, null, null),
3186  "word-break" => array(null, null, null, "-ms-word-break"),
3187  "word-wrap" => array(null, null, null, "-ms-word-wrap"),
3188  "writing-mode" => array(null, "-webkit-writing-mode", null, "-ms-writing-mode"),
3189  "zoom" => array(null, null, null, "-ms-zoom")
3190  );
3191  /**
3192  * Implements {@link aCssMinifierFilter::filter()}.
3193  *
3194  * @param array $tokens Array of objects of type aCssToken
3195  * @return integer Count of added, changed or removed tokens; a return value large than 0 will rebuild the array
3196  */
3197  public function apply(array &$tokens)
3198  {
3199  $r = 0;
3200  $transformations = &$this->transformations;
3201  for ($i = 0, $l = count($tokens); $i < $l; $i++)
3202  {
3203  if (get_class($tokens[$i]) === "CssRulesetDeclarationToken")
3204  {
3205  $tProperty = $tokens[$i]->Property;
3206  if (isset($transformations[$tProperty]))
3207  {
3208  $result = array();
3209  if (is_callable($transformations[$tProperty]))
3210  {
3211  $result = call_user_func_array($transformations[$tProperty], array($tokens[$i]));
3212  if (!is_array($result) && is_object($result))
3213  {
3214  $result = array($result);
3215  }
3216  }
3217  else
3218  {
3219  $tValue = $tokens[$i]->Value;
3220  $tMediaTypes = $tokens[$i]->MediaTypes;
3221  foreach ($transformations[$tProperty] as $property)
3222  {
3223  if ($property !== null)
3224  {
3225  $result[] = new CssRulesetDeclarationToken($property, $tValue, $tMediaTypes);
3226  }
3227  }
3228  }
3229  if (count($result) > 0)
3230  {
3231  array_splice($tokens, $i + 1, 0, $result);
3232  $i += count($result);
3233  $l += count($result);
3234  }
3235  }
3236  }
3237  }
3238  return $r;
3239  }
3240  /**
3241  * Transforms the Internet Explorer specific declaration property "filter" to Internet Explorer 8+ compatible
3242  * declaratiopn property "-ms-filter".
3243  *
3244  * @param aCssToken $token
3245  * @return array
3246  */
3247  private static function filter($token)
3248  {
3249  $r = array
3250  (
3251  new CssRulesetDeclarationToken("-ms-filter", "\"" . $token->Value . "\"", $token->MediaTypes),
3252  );
3253  return $r;
3254  }
3255  /**
3256  * Transforms "opacity: {value}" into browser specific counterparts.
3257  *
3258  * @param aCssToken $token
3259  * @return array
3260  */
3261  private static function opacity($token)
3262  {
3263  // Calculate the value for Internet Explorer filter statement
3264  $ieValue = (int) ((float) $token->Value * 100);
3265  $r = array
3266  (
3267  // Internet Explorer >= 8
3268  new CssRulesetDeclarationToken("-ms-filter", "\"alpha(opacity=" . $ieValue . ")\"", $token->MediaTypes),
3269  // Internet Explorer >= 4 <= 7
3270  new CssRulesetDeclarationToken("filter", "alpha(opacity=" . $ieValue . ")", $token->MediaTypes),
3271  new CssRulesetDeclarationToken("zoom", "1", $token->MediaTypes)
3272  );
3273  return $r;
3274  }
3275  /**
3276  * Transforms "white-space: pre-wrap" into browser specific counterparts.
3277  *
3278  * @param aCssToken $token
3279  * @return array
3280  */
3281  private static function whiteSpace($token)
3282  {
3283  if (strtolower($token->Value) === "pre-wrap")
3284  {
3285  $r = array
3286  (
3287  // Firefox < 3
3288  new CssRulesetDeclarationToken("white-space", "-moz-pre-wrap", $token->MediaTypes),
3289  // Webkit
3290  new CssRulesetDeclarationToken("white-space", "-webkit-pre-wrap", $token->MediaTypes),
3291  // Opera >= 4 <= 6
3292  new CssRulesetDeclarationToken("white-space", "-pre-wrap", $token->MediaTypes),
3293  // Opera >= 7
3294  new CssRulesetDeclarationToken("white-space", "-o-pre-wrap", $token->MediaTypes),
3295  // Internet Explorer >= 5.5
3296  new CssRulesetDeclarationToken("word-wrap", "break-word", $token->MediaTypes)
3297  );
3298  return $r;
3299  }
3300  else
3301  {
3302  return array();
3303  }
3304  }
3305  }
3306 
3307 /**
3308  * This {@link aCssMinifierFilter minifier filter} will convert @keyframes at-rule block to browser specific counterparts.
3309  *
3310  * @package CssMin/Minifier/Filters
3311  * @link http://code.google.com/p/cssmin/
3312  * @author Joe Scylla <joe.scylla@gmail.com>
3313  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3314  * @license http://opensource.org/licenses/mit-license.php MIT License
3315  * @version 3.0.1
3316  */
3318  {
3319  /**
3320  * Implements {@link aCssMinifierFilter::filter()}.
3321  *
3322  * @param array $tokens Array of objects of type aCssToken
3323  * @return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array
3324  */
3325  public function apply(array &$tokens)
3326  {
3327  $r = 0;
3328  $transformations = array("-moz-keyframes", "-webkit-keyframes");
3329  for ($i = 0, $l = count($tokens); $i < $l; $i++)
3330  {
3331  if (get_class($tokens[$i]) === "CssAtKeyframesStartToken")
3332  {
3333  for ($ii = $i; $ii < $l; $ii++)
3334  {
3335  if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken")
3336  {
3337  break;
3338  }
3339  }
3340  if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken")
3341  {
3342  $add = array();
3343  $source = array();
3344  for ($iii = $i; $iii <= $ii; $iii++)
3345  {
3346  $source[] = clone($tokens[$iii]);
3347  }
3348  foreach ($transformations as $transformation)
3349  {
3350  $t = array();
3351  foreach ($source as $token)
3352  {
3353  $t[] = clone($token);
3354  }
3355  $t[0]->AtRuleName = $transformation;
3356  $add = array_merge($add, $t);
3357  }
3358  if (isset($this->configuration["RemoveSource"]) && $this->configuration["RemoveSource"] === true)
3359  {
3360  array_splice($tokens, $i, $ii - $i + 1, $add);
3361  }
3362  else
3363  {
3364  array_splice($tokens, $ii + 1, 0, $add);
3365  }
3366  $l = count($tokens);
3367  $i = $ii + count($add);
3368  $r += count($add);
3369  }
3370  }
3371  }
3372  return $r;
3373  }
3374  }
3375 
3376 /**
3377  * This {@link aCssMinifierPlugin} will convert a color value in hsl notation to hexadecimal notation.
3378  *
3379  * Example:
3380  * <code>
3381  * color: hsl(232,36%,48%);
3382  * </code>
3383  *
3384  * Will get converted to:
3385  * <code>
3386  * color:#4e5aa7;
3387  * </code>
3388  *
3389  * @package CssMin/Minifier/Plugins
3390  * @link http://code.google.com/p/cssmin/
3391  * @author Joe Scylla <joe.scylla@gmail.com>
3392  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3393  * @license http://opensource.org/licenses/mit-license.php MIT License
3394  * @version 3.0.1
3395  */
3397  {
3398  /**
3399  * Regular expression matching the value.
3400  *
3401  * @var string
3402  */
3403  private $reMatch = "/^hsl\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*%\s*,\s*([0-9]+)\s*%\s*\)/iS";
3404  /**
3405  * Implements {@link aCssMinifierPlugin::minify()}.
3406  *
3407  * @param aCssToken $token Token to process
3408  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
3409  */
3410  public function apply(aCssToken &$token)
3411  {
3412  if (stripos($token->Value, "hsl") !== false && preg_match($this->reMatch, $token->Value, $m))
3413  {
3414  $token->Value = str_replace($m[0], $this->hsl2hex($m[1], $m[2], $m[3]), $token->Value);
3415  }
3416  return false;
3417  }
3418  /**
3419  * Implements {@link aMinifierPlugin::getTriggerTokens()}
3420  *
3421  * @return array
3422  */
3423  public function getTriggerTokens()
3424  {
3425  return array
3426  (
3427  "CssAtFontFaceDeclarationToken",
3428  "CssAtPageDeclarationToken",
3429  "CssRulesetDeclarationToken"
3430  );
3431  }
3432  /**
3433  * Convert a HSL value to hexadecimal notation.
3434  *
3435  * Based on: {@link http://www.easyrgb.com/index.php?X=MATH&H=19#text19}.
3436  *
3437  * @param integer $hue Hue
3438  * @param integer $saturation Saturation
3439  * @param integer $lightness Lightnesss
3440  * @return string
3441  */
3442  private function hsl2hex($hue, $saturation, $lightness)
3443  {
3444  $hue = $hue / 360;
3445  $saturation = $saturation / 100;
3446  $lightness = $lightness / 100;
3447  if ($saturation == 0)
3448  {
3449  $red = $lightness * 255;
3450  $green = $lightness * 255;
3451  $blue = $lightness * 255;
3452  }
3453  else
3454  {
3455  if ($lightness < 0.5 )
3456  {
3457  $v2 = $lightness * (1 + $saturation);
3458  }
3459  else
3460  {
3461  $v2 = ($lightness + $saturation) - ($saturation * $lightness);
3462  }
3463  $v1 = 2 * $lightness - $v2;
3464  $red = 255 * self::hue2rgb($v1, $v2, $hue + (1 / 3));
3465  $green = 255 * self::hue2rgb($v1, $v2, $hue);
3466  $blue = 255 * self::hue2rgb($v1, $v2, $hue - (1 / 3));
3467  }
3468  return "#" . str_pad(dechex(round($red)), 2, "0", STR_PAD_LEFT) . str_pad(dechex(round($green)), 2, "0", STR_PAD_LEFT) . str_pad(dechex(round($blue)), 2, "0", STR_PAD_LEFT);
3469  }
3470  /**
3471  * Apply hue to a rgb color value.
3472  *
3473  * @param integer $v1 Value 1
3474  * @param integer $v2 Value 2
3475  * @param integer $hue Hue
3476  * @return integer
3477  */
3478  private function hue2rgb($v1, $v2, $hue)
3479  {
3480  if ($hue < 0)
3481  {
3482  $hue += 1;
3483  }
3484  if ($hue > 1)
3485  {
3486  $hue -= 1;
3487  }
3488  if ((6 * $hue) < 1)
3489  {
3490  return ($v1 + ($v2 - $v1) * 6 * $hue);
3491  }
3492  if ((2 * $hue) < 1)
3493  {
3494  return ($v2);
3495  }
3496  if ((3 * $hue) < 2)
3497  {
3498  return ($v1 + ($v2 - $v1) * (( 2 / 3) - $hue) * 6);
3499  }
3500  return $v1;
3501  }
3502  }
3503 
3504 /**
3505  * This {@link aCssMinifierPlugin} will convert the font-weight values normal and bold to their numeric notation.
3506  *
3507  * Example:
3508  * <code>
3509  * font-weight: normal;
3510  * font: bold 11px monospace;
3511  * </code>
3512  *
3513  * Will get converted to:
3514  * <code>
3515  * font-weight:400;
3516  * font:700 11px monospace;
3517  * </code>
3518  *
3519  * @package CssMin/Minifier/Pluginsn
3520  * @link http://code.google.com/p/cssmin/
3521  * @author Joe Scylla <joe.scylla@gmail.com>
3522  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3523  * @license http://opensource.org/licenses/mit-license.php MIT License
3524  * @version 3.0.1
3525  */
3527  {
3528  /**
3529  * Array of included declaration properties this plugin will process; others declaration properties will get
3530  * ignored.
3531  *
3532  * @var array
3533  */
3534  private $include = array
3535  (
3536  "font",
3537  "font-weight"
3538  );
3539  /**
3540  * Regular expression matching the value.
3541  *
3542  * @var string
3543  */
3544  private $reMatch = null;
3545  /**
3546  * Regular expression replace the value.
3547  *
3548  * @var string
3549  */
3550  private $reReplace = "\"\${1}\" . \$this->transformation[\"\${2}\"] . \"\${3}\"";
3551  /**
3552  * Transformation table used by the {@link CssConvertFontWeightMinifierPlugin::$reReplace replace regular expression}.
3553  *
3554  * @var array
3555  */
3556  private $transformation = array
3557  (
3558  "normal" => "400",
3559  "bold" => "700"
3560  );
3561  /**
3562  * Overwrites {@link aCssMinifierPlugin::__construct()}.
3563  *
3564  * The constructor will create the {@link CssConvertFontWeightMinifierPlugin::$reReplace replace regular expression}
3565  * based on the {@link CssConvertFontWeightMinifierPlugin::$transformation transformation table}.
3566  *
3567  * @param CssMinifier $minifier The CssMinifier object of this plugin.
3568  * @return void
3569  */
3570  public function __construct(CssMinifier $minifier)
3571  {
3572  $this->reMatch = "/(^|\s)+(" . implode("|", array_keys($this->transformation)). ")(\s|$)+/eiS";
3573  parent::__construct($minifier);
3574  }
3575  /**
3576  * Implements {@link aCssMinifierPlugin::minify()}.
3577  *
3578  * @param aCssToken $token Token to process
3579  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
3580  */
3581  public function apply(aCssToken &$token)
3582  {
3583  if (in_array($token->Property, $this->include) && preg_match($this->reMatch, $token->Value, $m))
3584  {
3585  $token->Value = preg_replace($this->reMatch, $this->reReplace, $token->Value);
3586  }
3587  return false;
3588  }
3589  /**
3590  * Implements {@link aMinifierPlugin::getTriggerTokens()}
3591  *
3592  * @return array
3593  */
3594  public function getTriggerTokens()
3595  {
3596  return array
3597  (
3598  "CssAtFontFaceDeclarationToken",
3599  "CssAtPageDeclarationToken",
3600  "CssRulesetDeclarationToken"
3601  );
3602  }
3603  }
3604 
3605 /**
3606  * This {@link aCssMinifierPlugin} will compress several unit values to their short notations. Examples:
3607  *
3608  * <code>
3609  * padding: 0.5em;
3610  * border: 0px;
3611  * margin: 0 0 0 0;
3612  * </code>
3613  *
3614  * Will get compressed to:
3615  *
3616  * <code>
3617  * padding:.5px;
3618  * border:0;
3619  * margin:0;
3620  * </code>
3621  *
3622  * --
3623  *
3624  * @package CssMin/Minifier/Plugins
3625  * @link http://code.google.com/p/cssmin/
3626  * @author Joe Scylla <joe.scylla@gmail.com>
3627  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3628  * @license http://opensource.org/licenses/mit-license.php MIT License
3629  * @version 3.0.1
3630  */
3632  {
3633  /**
3634  * Regular expression used for matching and replacing unit values.
3635  *
3636  * @var array
3637  */
3638  private $re = array
3639  (
3640  "/(^| |-)0\.([0-9]+?)(0+)?(%|em|ex|px|in|cm|mm|pt|pc)/iS" => "\${1}.\${2}\${4}",
3641  "/(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/iS" => "\${1}0",
3642  "/(^0\s0\s0\s0)|(^0\s0\s0$)|(^0\s0$)/iS" => "0"
3643  );
3644  /**
3645  * Regular expression matching the value.
3646  *
3647  * @var string
3648  */
3649  private $reMatch = "/(^| |-)0\.([0-9]+?)(0+)?(%|em|ex|px|in|cm|mm|pt|pc)|(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)|(^0\s0\s0\s0$)|(^0\s0\s0$)|(^0\s0$)/iS";
3650  /**
3651  * Implements {@link aCssMinifierPlugin::minify()}.
3652  *
3653  * @param aCssToken $token Token to process
3654  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
3655  */
3656  public function apply(aCssToken &$token)
3657  {
3658  if (preg_match($this->reMatch, $token->Value))
3659  {
3660  foreach ($this->re as $reMatch => $reReplace)
3661  {
3662  $token->Value = preg_replace($reMatch, $reReplace, $token->Value);
3663  }
3664  }
3665  return false;
3666  }
3667  /**
3668  * Implements {@link aMinifierPlugin::getTriggerTokens()}
3669  *
3670  * @return array
3671  */
3672  public function getTriggerTokens()
3673  {
3674  return array
3675  (
3676  "CssAtFontFaceDeclarationToken",
3677  "CssAtPageDeclarationToken",
3678  "CssRulesetDeclarationToken"
3679  );
3680  }
3681  }
3682 
3683 /**
3684  * This {@link aCssMinifierPlugin} compress the content of expresssion() declaration values.
3685  *
3686  * For compression of expressions {@link https://github.com/rgrove/jsmin-php/ JSMin} will get used. JSMin have to be
3687  * already included or loadable via {@link http://goo.gl/JrW54 PHP autoloading}.
3688  *
3689  * @package CssMin/Minifier/Plugins
3690  * @link http://code.google.com/p/cssmin/
3691  * @author Joe Scylla <joe.scylla@gmail.com>
3692  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3693  * @license http://opensource.org/licenses/mit-license.php MIT License
3694  * @version 3.0.1
3695  */
3697  {
3698  /**
3699  * Implements {@link aCssMinifierPlugin::minify()}.
3700  *
3701  * @param aCssToken $token Token to process
3702  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
3703  */
3704  public function apply(aCssToken &$token)
3705  {
3706  if (class_exists("JSMin") && stripos($token->Value, "expression(") !== false)
3707  {
3708  $value = $token->Value;
3709  $value = substr($token->Value, stripos($token->Value, "expression(") + 10);
3710  $value = trim(JSMin::minify($value));
3711  $token->Value = "expression(" . $value . ")";
3712  }
3713  return false;
3714  }
3715  /**
3716  * Implements {@link aMinifierPlugin::getTriggerTokens()}
3717  *
3718  * @return array
3719  */
3720  public function getTriggerTokens()
3721  {
3722  return array
3723  (
3724  "CssAtFontFaceDeclarationToken",
3725  "CssAtPageDeclarationToken",
3726  "CssRulesetDeclarationToken"
3727  );
3728  }
3729  }
3730 
3731 /**
3732  * This {@link aCssMinifierPlugin} will convert hexadecimal color value with 6 chars to their 3 char hexadecimal
3733  * notation (if possible).
3734  *
3735  * Example:
3736  * <code>
3737  * color: #aabbcc;
3738  * </code>
3739  *
3740  * Will get converted to:
3741  * <code>
3742  * color:#abc;
3743  * </code>
3744  *
3745  * @package CssMin/Minifier/Plugins
3746  * @link http://code.google.com/p/cssmin/
3747  * @author Joe Scylla <joe.scylla@gmail.com>
3748  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3749  * @license http://opensource.org/licenses/mit-license.php MIT License
3750  * @version 3.0.1
3751  */
3753  {
3754  /**
3755  * Regular expression matching 6 char hexadecimal color values.
3756  *
3757  * @var string
3758  */
3759  private $reMatch = "/\#([0-9a-f]{6})/iS";
3760  /**
3761  * Implements {@link aCssMinifierPlugin::minify()}.
3762  *
3763  * @param aCssToken $token Token to process
3764  * @return boolean Return TRUE to break the processing of this token; FALSE to continue
3765  */
3766  public function apply(aCssToken &$token)
3767  {
3768  if (strpos($token->Value, "#") !== false && preg_match($this->reMatch, $token->Value, $m))
3769  {
3770  $value = strtolower($m[1]);
3771  if ($value[0] == $value[1] && $value[2] == $value[3] && $value[4] == $value[5])
3772  {
3773  $token->Value = str_replace($m[0], "#" . $value[0] . $value[2] . $value[4], $token->Value);
3774  }
3775  }
3776  return false;
3777  }
3778  /**
3779  * Implements {@link aMinifierPlugin::getTriggerTokens()}
3780  *
3781  * @return array
3782  */
3783  public function getTriggerTokens()
3784  {
3785  return array
3786  (
3787  "CssAtFontFaceDeclarationToken",
3788  "CssAtPageDeclarationToken",
3789  "CssRulesetDeclarationToken"
3790  );
3791  }
3792  }
3793 
3794 /**
3795  * This {@link aCssToken CSS token} represents a CSS comment.
3796  *
3797  * @package CssMin/Tokens
3798  * @link http://code.google.com/p/cssmin/
3799  * @author Joe Scylla <joe.scylla@gmail.com>
3800  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3801  * @license http://opensource.org/licenses/mit-license.php MIT License
3802  * @version 3.0.1
3803  */
3805  {
3806  /**
3807  * Comment as Text.
3808  *
3809  * @var string
3810  */
3811  public $Comment = "";
3812  /**
3813  * Set the properties of a comment token.
3814  *
3815  * @param string $comment Comment including comment delimiters
3816  * @return void
3817  */
3818  public function __construct($comment)
3819  {
3820  $this->Comment = $comment;
3821  }
3822  /**
3823  * Implements {@link aCssToken::__toString()}.
3824  *
3825  * @return string
3826  */
3827  public function __toString()
3828  {
3829  return $this->Comment;
3830  }
3831  }
3832 
3833 /**
3834  * {@link aCssParserPlugin Parser plugin} for parsing comments.
3835  *
3836  * Adds a {@link CssCommentToken} to the parser if a comment was found.
3837  *
3838  * @package CssMin/Parser/Plugins
3839  * @link http://code.google.com/p/cssmin/
3840  * @author Joe Scylla <joe.scylla@gmail.com>
3841  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3842  * @license http://opensource.org/licenses/mit-license.php MIT License
3843  * @version 3.0.1
3844  */
3846  {
3847  /**
3848  * Implements {@link aCssParserPlugin::getTriggerChars()}.
3849  *
3850  * @return array
3851  */
3852  public function getTriggerChars()
3853  {
3854  return array("*", "/");
3855  }
3856  /**
3857  * Implements {@link aCssParserPlugin::getTriggerStates()}.
3858  *
3859  * @return array
3860  */
3861  public function getTriggerStates()
3862  {
3863  return false;
3864  }
3865  /**
3866  * Stored buffer for restore.
3867  *
3868  * @var string
3869  */
3870  private $restoreBuffer = "";
3871  /**
3872  * Implements {@link aCssParserPlugin::parse()}.
3873  *
3874  * @param integer $index Current index
3875  * @param string $char Current char
3876  * @param string $previousChar Previous char
3877  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
3878  */
3879  public function parse($index, $char, $previousChar, $state)
3880  {
3881  if ($char === "*" && $previousChar === "/" && $state !== "T_COMMENT")
3882  {
3883  $this->parser->pushState("T_COMMENT");
3884  $this->parser->setExclusive(__CLASS__);
3885  $this->restoreBuffer = substr($this->parser->getAndClearBuffer(), 0, -2);
3886  }
3887  elseif ($char === "/" && $previousChar === "*" && $state === "T_COMMENT")
3888  {
3889  $this->parser->popState();
3890  $this->parser->unsetExclusive();
3891  $this->parser->appendToken(new CssCommentToken("/*" . $this->parser->getAndClearBuffer()));
3892  $this->parser->setBuffer($this->restoreBuffer);
3893  }
3894  else
3895  {
3896  return false;
3897  }
3898  return true;
3899  }
3900  }
3901 
3902 /**
3903  * This {@link aCssToken CSS token} represents the start of a @variables at-rule block.
3904  *
3905  * @package CssMin/Tokens
3906  * @link http://code.google.com/p/cssmin/
3907  * @author Joe Scylla <joe.scylla@gmail.com>
3908  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3909  * @license http://opensource.org/licenses/mit-license.php MIT License
3910  * @version 3.0.1
3911  */
3913  {
3914  /**
3915  * Media types of the @variables at-rule block.
3916  *
3917  * @var array
3918  */
3919  public $MediaTypes = array();
3920  /**
3921  * Set the properties of a @variables at-rule token.
3922  *
3923  * @param array $mediaTypes Media types
3924  * @return void
3925  */
3926  public function __construct($mediaTypes = null)
3927  {
3928  $this->MediaTypes = $mediaTypes ? $mediaTypes : array("all");
3929  }
3930  /**
3931  * Implements {@link aCssToken::__toString()}.
3932  *
3933  * @return string
3934  */
3935  public function __toString()
3936  {
3937  return "";
3938  }
3939  }
3940 
3941 /**
3942  * {@link aCssParserPlugin Parser plugin} for parsing @variables at-rule block with including declarations.
3943  *
3944  * Found @variables at-rule blocks will add a {@link CssAtVariablesStartToken} and {@link CssAtVariablesEndToken} to the
3945  * parser; including declarations as {@link CssAtVariablesDeclarationToken}.
3946  *
3947  * @package CssMin/Parser/Plugins
3948  * @link http://code.google.com/p/cssmin/
3949  * @author Joe Scylla <joe.scylla@gmail.com>
3950  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
3951  * @license http://opensource.org/licenses/mit-license.php MIT License
3952  * @version 3.0.1
3953  */
3955  {
3956  /**
3957  * Implements {@link aCssParserPlugin::getTriggerChars()}.
3958  *
3959  * @return array
3960  */
3961  public function getTriggerChars()
3962  {
3963  return array("@", "{", "}", ":", ";");
3964  }
3965  /**
3966  * Implements {@link aCssParserPlugin::getTriggerStates()}.
3967  *
3968  * @return array
3969  */
3970  public function getTriggerStates()
3971  {
3972  return array("T_DOCUMENT", "T_AT_VARIABLES::PREPARE", "T_AT_VARIABLES", "T_AT_VARIABLES_DECLARATION");
3973  }
3974  /**
3975  * Implements {@link aCssParserPlugin::parse()}.
3976  *
3977  * @param integer $index Current index
3978  * @param string $char Current char
3979  * @param string $previousChar Previous char
3980  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
3981  */
3982  public function parse($index, $char, $previousChar, $state)
3983  {
3984  // Start of @variables at-rule block
3985  if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@variables")
3986  {
3987  $this->parser->pushState("T_AT_VARIABLES::PREPARE");
3988  $this->parser->clearBuffer();
3989  return $index + 10;
3990  }
3991  // Start of @variables declarations
3992  elseif ($char === "{" && $state === "T_AT_VARIABLES::PREPARE")
3993  {
3994  $this->parser->setState("T_AT_VARIABLES");
3995  $mediaTypes = array_filter(array_map("trim", explode(",", $this->parser->getAndClearBuffer("{"))));
3996  $this->parser->appendToken(new CssAtVariablesStartToken($mediaTypes));
3997  }
3998  // Start of @variables declaration
3999  if ($char === ":" && $state === "T_AT_VARIABLES")
4000  {
4001  $this->buffer = $this->parser->getAndClearBuffer(":");
4002  $this->parser->pushState("T_AT_VARIABLES_DECLARATION");
4003  }
4004  // Unterminated @variables declaration
4005  elseif ($char === ":" && $state === "T_AT_VARIABLES_DECLARATION")
4006  {
4007  // Ignore Internet Explorer filter declarations
4008  if ($this->buffer === "filter")
4009  {
4010  return false;
4011  }
4012  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @variables declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
4013  }
4014  // End of @variables declaration
4015  elseif (($char === ";" || $char === "}") && $state === "T_AT_VARIABLES_DECLARATION")
4016  {
4017  $value = $this->parser->getAndClearBuffer(";}");
4018  if (strtolower(substr($value, -10, 10)) === "!important")
4019  {
4020  $value = trim(substr($value, 0, -10));
4021  $isImportant = true;
4022  }
4023  else
4024  {
4025  $isImportant = false;
4026  }
4027  $this->parser->popState();
4028  $this->parser->appendToken(new CssAtVariablesDeclarationToken($this->buffer, $value, $isImportant));
4029  $this->buffer = "";
4030  }
4031  // End of @variables at-rule block
4032  elseif ($char === "}" && $state === "T_AT_VARIABLES")
4033  {
4034  $this->parser->popState();
4035  $this->parser->clearBuffer();
4036  $this->parser->appendToken(new CssAtVariablesEndToken());
4037  }
4038  else
4039  {
4040  return false;
4041  }
4042  return true;
4043  }
4044  }
4045 
4046 /**
4047  * This {@link aCssToken CSS token} represents the end of a @variables at-rule block.
4048  *
4049  * @package CssMin/Tokens
4050  * @link http://code.google.com/p/cssmin/
4051  * @author Joe Scylla <joe.scylla@gmail.com>
4052  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4053  * @license http://opensource.org/licenses/mit-license.php MIT License
4054  * @version 3.0.1
4055  */
4057  {
4058  /**
4059  * Implements {@link aCssToken::__toString()}.
4060  *
4061  * @return string
4062  */
4063  public function __toString()
4064  {
4065  return "";
4066  }
4067  }
4068 
4069 /**
4070  * This {@link aCssToken CSS token} represents a declaration of a @variables at-rule block.
4071  *
4072  * @package CssMin/Tokens
4073  * @link http://code.google.com/p/cssmin/
4074  * @author Joe Scylla <joe.scylla@gmail.com>
4075  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4076  * @license http://opensource.org/licenses/mit-license.php MIT License
4077  * @version 3.0.1
4078  */
4080  {
4081  /**
4082  * Implements {@link aCssToken::__toString()}.
4083  *
4084  * @return string
4085  */
4086  public function __toString()
4087  {
4088  return "";
4089  }
4090  }
4091 
4092 /**
4093 * This {@link aCssToken CSS token} represents the start of a @page at-rule block.
4094  *
4095  * @package CssMin/Tokens
4096  * @link http://code.google.com/p/cssmin/
4097  * @author Joe Scylla <joe.scylla@gmail.com>
4098  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4099  * @license http://opensource.org/licenses/mit-license.php MIT License
4100  * @version 3.0.1
4101  */
4103  {
4104  /**
4105  * Selector.
4106  *
4107  * @var string
4108  */
4109  public $Selector = "";
4110  /**
4111  * Sets the properties of the @page at-rule.
4112  *
4113  * @param string $selector Selector
4114  * @return void
4115  */
4116  public function __construct($selector = "")
4117  {
4118  $this->Selector = $selector;
4119  }
4120  /**
4121  * Implements {@link aCssToken::__toString()}.
4122  *
4123  * @return string
4124  */
4125  public function __toString()
4126  {
4127  return "@page" . ($this->Selector ? " " . $this->Selector : "") . "{";
4128  }
4129  }
4130 
4131 /**
4132  * {@link aCssParserPlugin Parser plugin} for parsing @page at-rule block with including declarations.
4133  *
4134  * Found @page at-rule blocks will add a {@link CssAtPageStartToken} and {@link CssAtPageEndToken} to the
4135  * parser; including declarations as {@link CssAtPageDeclarationToken}.
4136  *
4137  * @package CssMin/Parser/Plugins
4138  * @link http://code.google.com/p/cssmin/
4139  * @author Joe Scylla <joe.scylla@gmail.com>
4140  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4141  * @license http://opensource.org/licenses/mit-license.php MIT License
4142  * @version 3.0.1
4143  */
4145  {
4146  /**
4147  * Implements {@link aCssParserPlugin::getTriggerChars()}.
4148  *
4149  * @return array
4150  */
4151  public function getTriggerChars()
4152  {
4153  return array("@", "{", "}", ":", ";");
4154  }
4155  /**
4156  * Implements {@link aCssParserPlugin::getTriggerStates()}.
4157  *
4158  * @return array
4159  */
4160  public function getTriggerStates()
4161  {
4162  return array("T_DOCUMENT", "T_AT_PAGE::SELECTOR", "T_AT_PAGE", "T_AT_PAGE_DECLARATION");
4163  }
4164  /**
4165  * Implements {@link aCssParserPlugin::parse()}.
4166  *
4167  * @param integer $index Current index
4168  * @param string $char Current char
4169  * @param string $previousChar Previous char
4170  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
4171  */
4172  public function parse($index, $char, $previousChar, $state)
4173  {
4174  // Start of @page at-rule block
4175  if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 5)) === "@page")
4176  {
4177  $this->parser->pushState("T_AT_PAGE::SELECTOR");
4178  $this->parser->clearBuffer();
4179  return $index + 5;
4180  }
4181  // Start of @page declarations
4182  elseif ($char === "{" && $state === "T_AT_PAGE::SELECTOR")
4183  {
4184  $selector = $this->parser->getAndClearBuffer("{");
4185  $this->parser->setState("T_AT_PAGE");
4186  $this->parser->clearBuffer();
4187  $this->parser->appendToken(new CssAtPageStartToken($selector));
4188  }
4189  // Start of @page declaration
4190  elseif ($char === ":" && $state === "T_AT_PAGE")
4191  {
4192  $this->parser->pushState("T_AT_PAGE_DECLARATION");
4193  $this->buffer = $this->parser->getAndClearBuffer(":", true);
4194  }
4195  // Unterminated @font-face declaration
4196  elseif ($char === ":" && $state === "T_AT_PAGE_DECLARATION")
4197  {
4198  // Ignore Internet Explorer filter declarations
4199  if ($this->buffer === "filter")
4200  {
4201  return false;
4202  }
4203  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @page declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
4204  }
4205  // End of @page declaration
4206  elseif (($char === ";" || $char === "}") && $state == "T_AT_PAGE_DECLARATION")
4207  {
4208  $value = $this->parser->getAndClearBuffer(";}");
4209  if (strtolower(substr($value, -10, 10)) == "!important")
4210  {
4211  $value = trim(substr($value, 0, -10));
4212  $isImportant = true;
4213  }
4214  else
4215  {
4216  $isImportant = false;
4217  }
4218  $this->parser->popState();
4219  $this->parser->appendToken(new CssAtPageDeclarationToken($this->buffer, $value, $isImportant));
4220  // --
4221  if ($char === "}")
4222  {
4223  $this->parser->popState();
4224  $this->parser->appendToken(new CssAtPageEndToken());
4225  }
4226  $this->buffer = "";
4227  }
4228  // End of @page at-rule block
4229  elseif ($char === "}" && $state === "T_AT_PAGE")
4230  {
4231  $this->parser->popState();
4232  $this->parser->clearBuffer();
4233  $this->parser->appendToken(new CssAtPageEndToken());
4234  }
4235  else
4236  {
4237  return false;
4238  }
4239  return true;
4240  }
4241  }
4242 
4243 /**
4244  * This {@link aCssToken CSS token} represents the end of a @page at-rule block.
4245  *
4246  * @package CssMin/Tokens
4247  * @link http://code.google.com/p/cssmin/
4248  * @author Joe Scylla <joe.scylla@gmail.com>
4249  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4250  * @license http://opensource.org/licenses/mit-license.php MIT License
4251  * @version 3.0.1
4252  */
4254  {
4255 
4256  }
4257 
4258 /**
4259  * This {@link aCssToken CSS token} represents a declaration of a @page at-rule block.
4260  *
4261  * @package CssMin/Tokens
4262  * @link http://code.google.com/p/cssmin/
4263  * @author Joe Scylla <joe.scylla@gmail.com>
4264  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4265  * @license http://opensource.org/licenses/mit-license.php MIT License
4266  * @version 3.0.1
4267  */
4269  {
4270 
4271  }
4272 
4273 /**
4274  * This {@link aCssToken CSS token} represents the start of a @media at-rule block.
4275  *
4276  * @package CssMin/Tokens
4277  * @link http://code.google.com/p/cssmin/
4278  * @author Joe Scylla <joe.scylla@gmail.com>
4279  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4280  * @license http://opensource.org/licenses/mit-license.php MIT License
4281  * @version 3.0.1
4282  */
4284  {
4285  /**
4286  * Sets the properties of the @media at-rule.
4287  *
4288  * @param array $mediaTypes Media types
4289  * @return void
4290  */
4291  public function __construct(array $mediaTypes = array())
4292  {
4293  $this->MediaTypes = $mediaTypes;
4294  }
4295  /**
4296  * Implements {@link aCssToken::__toString()}.
4297  *
4298  * @return string
4299  */
4300  public function __toString()
4301  {
4302  return "@media " . implode(",", $this->MediaTypes) . "{";
4303  }
4304  }
4305 
4306 /**
4307  * {@link aCssParserPlugin Parser plugin} for parsing @media at-rule block.
4308  *
4309  * Found @media at-rule blocks will add a {@link CssAtMediaStartToken} and {@link CssAtMediaEndToken} to the parser.
4310  * This plugin will also set the the current media types using {@link CssParser::setMediaTypes()} and
4311  * {@link CssParser::unsetMediaTypes()}.
4312  *
4313  * @package CssMin/Parser/Plugins
4314  * @link http://code.google.com/p/cssmin/
4315  * @author Joe Scylla <joe.scylla@gmail.com>
4316  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4317  * @license http://opensource.org/licenses/mit-license.php MIT License
4318  * @version 3.0.1
4319  */
4321  {
4322  /**
4323  * Implements {@link aCssParserPlugin::getTriggerChars()}.
4324  *
4325  * @return array
4326  */
4327  public function getTriggerChars()
4328  {
4329  return array("@", "{", "}");
4330  }
4331  /**
4332  * Implements {@link aCssParserPlugin::getTriggerStates()}.
4333  *
4334  * @return array
4335  */
4336  public function getTriggerStates()
4337  {
4338  return array("T_DOCUMENT", "T_AT_MEDIA::PREPARE", "T_AT_MEDIA");
4339  }
4340  /**
4341  * Implements {@link aCssParserPlugin::parse()}.
4342  *
4343  * @param integer $index Current index
4344  * @param string $char Current char
4345  * @param string $previousChar Previous char
4346  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
4347  */
4348  public function parse($index, $char, $previousChar, $state)
4349  {
4350  if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 6)) === "@media")
4351  {
4352  $this->parser->pushState("T_AT_MEDIA::PREPARE");
4353  $this->parser->clearBuffer();
4354  return $index + 6;
4355  }
4356  elseif ($char === "{" && $state === "T_AT_MEDIA::PREPARE")
4357  {
4358  $mediaTypes = array_filter(array_map("trim", explode(",", $this->parser->getAndClearBuffer("{"))));
4359  $this->parser->setMediaTypes($mediaTypes);
4360  $this->parser->setState("T_AT_MEDIA");
4361  $this->parser->appendToken(new CssAtMediaStartToken($mediaTypes));
4362  }
4363  elseif ($char === "}" && $state === "T_AT_MEDIA")
4364  {
4365  $this->parser->appendToken(new CssAtMediaEndToken());
4366  $this->parser->clearBuffer();
4367  $this->parser->unsetMediaTypes();
4368  $this->parser->popState();
4369  }
4370  else
4371  {
4372  return false;
4373  }
4374  return true;
4375  }
4376  }
4377 
4378 /**
4379  * This {@link aCssToken CSS token} represents the end of a @media at-rule block.
4380  *
4381  * @package CssMin/Tokens
4382  * @link http://code.google.com/p/cssmin/
4383  * @author Joe Scylla <joe.scylla@gmail.com>
4384  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4385  * @license http://opensource.org/licenses/mit-license.php MIT License
4386  * @version 3.0.1
4387  */
4389  {
4390 
4391  }
4392 
4393 /**
4394  * This {@link aCssToken CSS token} represents the start of a @keyframes at-rule block.
4395  *
4396  * @package CssMin/Tokens
4397  * @link http://code.google.com/p/cssmin/
4398  * @author Joe Scylla <joe.scylla@gmail.com>
4399  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4400  * @license http://opensource.org/licenses/mit-license.php MIT License
4401  * @version 3.0.1
4402  */
4404  {
4405  /**
4406  * Name of the at-rule.
4407  *
4408  * @var string
4409  */
4410  public $AtRuleName = "keyframes";
4411  /**
4412  * Name
4413  *
4414  * @var string
4415  */
4416  public $Name = "";
4417  /**
4418  * Sets the properties of the @page at-rule.
4419  *
4420  * @param string $selector Selector
4421  * @return void
4422  */
4423  public function __construct($name, $atRuleName = null)
4424  {
4425  $this->Name = $name;
4426  if (!is_null($atRuleName))
4427  {
4428  $this->AtRuleName = $atRuleName;
4429  }
4430  }
4431  /**
4432  * Implements {@link aCssToken::__toString()}.
4433  *
4434  * @return string
4435  */
4436  public function __toString()
4437  {
4438  return "@" . $this->AtRuleName . " \"" . $this->Name . "\"{";
4439  }
4440  }
4441 
4442 /**
4443  * This {@link aCssToken CSS token} represents the start of a ruleset of a @keyframes at-rule block.
4444  *
4445  * @package CssMin/Tokens
4446  * @link http://code.google.com/p/cssmin/
4447  * @author Joe Scylla <joe.scylla@gmail.com>
4448  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4449  * @license http://opensource.org/licenses/mit-license.php MIT License
4450  * @version 3.0.1
4451  */
4453  {
4454  /**
4455  * Array of selectors.
4456  *
4457  * @var array
4458  */
4459  public $Selectors = array();
4460  /**
4461  * Set the properties of a ruleset token.
4462  *
4463  * @param array $selectors Selectors of the ruleset
4464  * @return void
4465  */
4466  public function __construct(array $selectors = array())
4467  {
4468  $this->Selectors = $selectors;
4469  }
4470  /**
4471  * Implements {@link aCssToken::__toString()}.
4472  *
4473  * @return string
4474  */
4475  public function __toString()
4476  {
4477  return implode(",", $this->Selectors) . "{";
4478  }
4479  }
4480 
4481 /**
4482  * This {@link aCssToken CSS token} represents the end of a ruleset of a @keyframes at-rule block.
4483  *
4484  * @package CssMin/Tokens
4485  * @link http://code.google.com/p/cssmin/
4486  * @author Joe Scylla <joe.scylla@gmail.com>
4487  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4488  * @license http://opensource.org/licenses/mit-license.php MIT License
4489  * @version 3.0.1
4490  */
4492  {
4493 
4494  }
4495 
4496 /**
4497  * This {@link aCssToken CSS token} represents a ruleset declaration of a @keyframes at-rule block.
4498  *
4499  * @package CssMin/Tokens
4500  * @link http://code.google.com/p/cssmin/
4501  * @author Joe Scylla <joe.scylla@gmail.com>
4502  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4503  * @license http://opensource.org/licenses/mit-license.php MIT License
4504  * @version 3.0.1
4505  */
4507  {
4508 
4509  }
4510 
4511 /**
4512  * {@link aCssParserPlugin Parser plugin} for parsing @keyframes at-rule blocks, rulesets and declarations.
4513  *
4514  * @package CssMin/Parser/Plugins
4515  * @link http://code.google.com/p/cssmin/
4516  * @author Joe Scylla <joe.scylla@gmail.com>
4517  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4518  * @license http://opensource.org/licenses/mit-license.php MIT License
4519  * @version 3.0.1
4520  */
4522  {
4523  /**
4524  * @var string Keyword
4525  */
4526  private $atRuleName = "";
4527  /**
4528  * Selectors.
4529  *
4530  * @var array
4531  */
4532  private $selectors = array();
4533  /**
4534  * Implements {@link aCssParserPlugin::getTriggerChars()}.
4535  *
4536  * @return array
4537  */
4538  public function getTriggerChars()
4539  {
4540  return array("@", "{", "}", ":", ",", ";");
4541  }
4542  /**
4543  * Implements {@link aCssParserPlugin::getTriggerStates()}.
4544  *
4545  * @return array
4546  */
4547  public function getTriggerStates()
4548  {
4549  return array("T_DOCUMENT", "T_AT_KEYFRAMES::NAME", "T_AT_KEYFRAMES", "T_AT_KEYFRAMES_RULESETS", "T_AT_KEYFRAMES_RULESET", "T_AT_KEYFRAMES_RULESET_DECLARATION");
4550  }
4551  /**
4552  * Implements {@link aCssParserPlugin::parse()}.
4553  *
4554  * @param integer $index Current index
4555  * @param string $char Current char
4556  * @param string $previousChar Previous char
4557  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
4558  */
4559  public function parse($index, $char, $previousChar, $state)
4560  {
4561  // Start of @keyframes at-rule block
4562  if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@keyframes")
4563  {
4564  $this->atRuleName = "keyframes";
4565  $this->parser->pushState("T_AT_KEYFRAMES::NAME");
4566  $this->parser->clearBuffer();
4567  return $index + 10;
4568  }
4569  // Start of @keyframes at-rule block (@-moz-keyframes)
4570  elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 15)) === "@-moz-keyframes")
4571  {
4572  $this->atRuleName = "-moz-keyframes";
4573  $this->parser->pushState("T_AT_KEYFRAMES::NAME");
4574  $this->parser->clearBuffer();
4575  return $index + 15;
4576  }
4577  // Start of @keyframes at-rule block (@-webkit-keyframes)
4578  elseif ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 18)) === "@-webkit-keyframes")
4579  {
4580  $this->atRuleName = "-webkit-keyframes";
4581  $this->parser->pushState("T_AT_KEYFRAMES::NAME");
4582  $this->parser->clearBuffer();
4583  return $index + 18;
4584  }
4585  // Start of @keyframes rulesets
4586  elseif ($char === "{" && $state === "T_AT_KEYFRAMES::NAME")
4587  {
4588  $name = $this->parser->getAndClearBuffer("{\"'");
4589  $this->parser->setState("T_AT_KEYFRAMES_RULESETS");
4590  $this->parser->clearBuffer();
4591  $this->parser->appendToken(new CssAtKeyframesStartToken($name, $this->atRuleName));
4592  }
4593  // Start of @keyframe ruleset and selectors
4594  if ($char === "," && $state === "T_AT_KEYFRAMES_RULESETS")
4595  {
4596  $this->selectors[] = $this->parser->getAndClearBuffer(",{");
4597  }
4598  // Start of a @keyframes ruleset
4599  elseif ($char === "{" && $state === "T_AT_KEYFRAMES_RULESETS")
4600  {
4601  if ($this->parser->getBuffer() !== "")
4602  {
4603  $this->selectors[] = $this->parser->getAndClearBuffer(",{");
4604  $this->parser->pushState("T_AT_KEYFRAMES_RULESET");
4605  $this->parser->appendToken(new CssAtKeyframesRulesetStartToken($this->selectors));
4606  $this->selectors = array();
4607  }
4608  }
4609  // Start of @keyframes ruleset declaration
4610  elseif ($char === ":" && $state === "T_AT_KEYFRAMES_RULESET")
4611  {
4612  $this->parser->pushState("T_AT_KEYFRAMES_RULESET_DECLARATION");
4613  $this->buffer = $this->parser->getAndClearBuffer(":;", true);
4614  }
4615  // Unterminated @keyframes ruleset declaration
4616  elseif ($char === ":" && $state === "T_AT_KEYFRAMES_RULESET_DECLARATION")
4617  {
4618  // Ignore Internet Explorer filter declarations
4619  if ($this->buffer === "filter")
4620  {
4621  return false;
4622  }
4623  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @keyframes ruleset declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
4624  }
4625  // End of declaration
4626  elseif (($char === ";" || $char === "}") && $state === "T_AT_KEYFRAMES_RULESET_DECLARATION")
4627  {
4628  $value = $this->parser->getAndClearBuffer(";}");
4629  if (strtolower(substr($value, -10, 10)) === "!important")
4630  {
4631  $value = trim(substr($value, 0, -10));
4632  $isImportant = true;
4633  }
4634  else
4635  {
4636  $isImportant = false;
4637  }
4638  $this->parser->popState();
4639  $this->parser->appendToken(new CssAtKeyframesRulesetDeclarationToken($this->buffer, $value, $isImportant));
4640  // Declaration ends with a right curly brace; so we have to end the ruleset
4641  if ($char === "}")
4642  {
4643  $this->parser->appendToken(new CssAtKeyframesRulesetEndToken());
4644  $this->parser->popState();
4645  }
4646  $this->buffer = "";
4647  }
4648  // End of @keyframes ruleset
4649  elseif ($char === "}" && $state === "T_AT_KEYFRAMES_RULESET")
4650  {
4651  $this->parser->clearBuffer();
4652 
4653  $this->parser->popState();
4654  $this->parser->appendToken(new CssAtKeyframesRulesetEndToken());
4655  }
4656  // End of @keyframes rulesets
4657  elseif ($char === "}" && $state === "T_AT_KEYFRAMES_RULESETS")
4658  {
4659  $this->parser->clearBuffer();
4660  $this->parser->popState();
4661  $this->parser->appendToken(new CssAtKeyframesEndToken());
4662  }
4663  else
4664  {
4665  return false;
4666  }
4667  return true;
4668  }
4669  }
4670 
4671 /**
4672  * This {@link aCssToken CSS token} represents the end of a @keyframes at-rule block.
4673  *
4674  * @package CssMin/Tokens
4675  * @link http://code.google.com/p/cssmin/
4676  * @author Joe Scylla <joe.scylla@gmail.com>
4677  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4678  * @license http://opensource.org/licenses/mit-license.php MIT License
4679  * @version 3.0.1
4680  */
4682  {
4683 
4684  }
4685 
4686 /**
4687  * This {@link aCssToken CSS token} represents a @import at-rule.
4688  *
4689  * @package CssMin/Tokens
4690  * @link http://code.google.com/p/cssmin/
4691  * @author Joe Scylla <joe.scylla@gmail.com>
4692  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4693  * @license http://opensource.org/licenses/mit-license.php MIT License
4694  * @version 3.0.1.b1 (2001-02-22)
4695  */
4697  {
4698  /**
4699  * Import path of the @import at-rule.
4700  *
4701  * @var string
4702  */
4703  public $Import = "";
4704  /**
4705  * Media types of the @import at-rule.
4706  *
4707  * @var array
4708  */
4709  public $MediaTypes = array();
4710  /**
4711  * Set the properties of a @import at-rule token.
4712  *
4713  * @param string $import Import path
4714  * @param array $mediaTypes Media types
4715  * @return void
4716  */
4717  public function __construct($import, $mediaTypes)
4718  {
4719  $this->Import = $import;
4720  $this->MediaTypes = $mediaTypes ? $mediaTypes : array();
4721  }
4722  /**
4723  * Implements {@link aCssToken::__toString()}.
4724  *
4725  * @return string
4726  */
4727  public function __toString()
4728  {
4729  return "@import \"" . $this->Import . "\"" . (count($this->MediaTypes) > 0 ? " " . implode(",", $this->MediaTypes) : ""). ";";
4730  }
4731  }
4732 
4733 /**
4734  * {@link aCssParserPlugin Parser plugin} for parsing @import at-rule.
4735  *
4736  * If a @import at-rule was found this plugin will add a {@link CssAtImportToken} to the parser.
4737  *
4738  * @package CssMin/Parser/Plugins
4739  * @link http://code.google.com/p/cssmin/
4740  * @author Joe Scylla <joe.scylla@gmail.com>
4741  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4742  * @license http://opensource.org/licenses/mit-license.php MIT License
4743  * @version 3.0.1
4744  */
4746  {
4747  /**
4748  * Implements {@link aCssParserPlugin::getTriggerChars()}.
4749  *
4750  * @return array
4751  */
4752  public function getTriggerChars()
4753  {
4754  return array("@", ";", ",", "\n");
4755  }
4756  /**
4757  * Implements {@link aCssParserPlugin::getTriggerStates()}.
4758  *
4759  * @return array
4760  */
4761  public function getTriggerStates()
4762  {
4763  return array("T_DOCUMENT", "T_AT_IMPORT");
4764  }
4765  /**
4766  * Implements {@link aCssParserPlugin::parse()}.
4767  *
4768  * @param integer $index Current index
4769  * @param string $char Current char
4770  * @param string $previousChar Previous char
4771  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
4772  */
4773  public function parse($index, $char, $previousChar, $state)
4774  {
4775  if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 7)) === "@import")
4776  {
4777  $this->parser->pushState("T_AT_IMPORT");
4778  $this->parser->clearBuffer();
4779  return $index + 7;
4780  }
4781  elseif (($char === ";" || $char === "\n") && $state === "T_AT_IMPORT")
4782  {
4783  $this->buffer = $this->parser->getAndClearBuffer(";");
4784  $pos = false;
4785  foreach (array(")", "\"", "'") as $needle)
4786  {
4787  if (($pos = strrpos($this->buffer, $needle)) !== false)
4788  {
4789  break;
4790  }
4791  }
4792  $import = substr($this->buffer, 0, $pos + 1);
4793  if (stripos($import, "url(") === 0)
4794  {
4795  $import = substr($import, 4, -1);
4796  }
4797  $import = trim($import, " \t\n\r\0\x0B'\"");
4798  $mediaTypes = array_filter(array_map("trim", explode(",", trim(substr($this->buffer, $pos + 1), " \t\n\r\0\x0B{"))));
4799  if ($pos)
4800  {
4801  $this->parser->appendToken(new CssAtImportToken($import, $mediaTypes));
4802  }
4803  else
4804  {
4805  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Invalid @import at-rule syntax", $this->parser->buffer));
4806  }
4807  $this->parser->popState();
4808  }
4809  else
4810  {
4811  return false;
4812  }
4813  return true;
4814  }
4815  }
4816 
4817 /**
4818  * This {@link aCssToken CSS token} represents the start of a @font-face at-rule block.
4819  *
4820  * @package CssMin/Tokens
4821  * @link http://code.google.com/p/cssmin/
4822  * @author Joe Scylla <joe.scylla@gmail.com>
4823  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4824  * @license http://opensource.org/licenses/mit-license.php MIT License
4825  * @version 3.0.1
4826  */
4828  {
4829  /**
4830  * Implements {@link aCssToken::__toString()}.
4831  *
4832  * @return string
4833  */
4834  public function __toString()
4835  {
4836  return "@font-face{";
4837  }
4838  }
4839 
4840 /**
4841  * {@link aCssParserPlugin Parser plugin} for parsing @font-face at-rule block with including declarations.
4842  *
4843  * Found @font-face at-rule blocks will add a {@link CssAtFontFaceStartToken} and {@link CssAtFontFaceEndToken} to the
4844  * parser; including declarations as {@link CssAtFontFaceDeclarationToken}.
4845  *
4846  * @package CssMin/Parser/Plugins
4847  * @link http://code.google.com/p/cssmin/
4848  * @author Joe Scylla <joe.scylla@gmail.com>
4849  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4850  * @license http://opensource.org/licenses/mit-license.php MIT License
4851  * @version 3.0.1
4852  */
4854  {
4855  /**
4856  * Implements {@link aCssParserPlugin::getTriggerChars()}.
4857  *
4858  * @return array
4859  */
4860  public function getTriggerChars()
4861  {
4862  return array("@", "{", "}", ":", ";");
4863  }
4864  /**
4865  * Implements {@link aCssParserPlugin::getTriggerStates()}.
4866  *
4867  * @return array
4868  */
4869  public function getTriggerStates()
4870  {
4871  return array("T_DOCUMENT", "T_AT_FONT_FACE::PREPARE", "T_AT_FONT_FACE", "T_AT_FONT_FACE_DECLARATION");
4872  }
4873  /**
4874  * Implements {@link aCssParserPlugin::parse()}.
4875  *
4876  * @param integer $index Current index
4877  * @param string $char Current char
4878  * @param string $previousChar Previous char
4879  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
4880  */
4881  public function parse($index, $char, $previousChar, $state)
4882  {
4883  // Start of @font-face at-rule block
4884  if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 10)) === "@font-face")
4885  {
4886  $this->parser->pushState("T_AT_FONT_FACE::PREPARE");
4887  $this->parser->clearBuffer();
4888  return $index + 10;
4889  }
4890  // Start of @font-face declarations
4891  elseif ($char === "{" && $state === "T_AT_FONT_FACE::PREPARE")
4892  {
4893  $this->parser->setState("T_AT_FONT_FACE");
4894  $this->parser->clearBuffer();
4895  $this->parser->appendToken(new CssAtFontFaceStartToken());
4896  }
4897  // Start of @font-face declaration
4898  elseif ($char === ":" && $state === "T_AT_FONT_FACE")
4899  {
4900  $this->parser->pushState("T_AT_FONT_FACE_DECLARATION");
4901  $this->buffer = $this->parser->getAndClearBuffer(":", true);
4902  }
4903  // Unterminated @font-face declaration
4904  elseif ($char === ":" && $state === "T_AT_FONT_FACE_DECLARATION")
4905  {
4906  // Ignore Internet Explorer filter declarations
4907  if ($this->buffer === "filter")
4908  {
4909  return false;
4910  }
4911  CssMin::triggerError(new CssError(__FILE__, __LINE__, __METHOD__ . ": Unterminated @font-face declaration", $this->buffer . ":" . $this->parser->getBuffer() . "_"));
4912  }
4913  // End of @font-face declaration
4914  elseif (($char === ";" || $char === "}") && $state === "T_AT_FONT_FACE_DECLARATION")
4915  {
4916  $value = $this->parser->getAndClearBuffer(";}");
4917  if (strtolower(substr($value, -10, 10)) === "!important")
4918  {
4919  $value = trim(substr($value, 0, -10));
4920  $isImportant = true;
4921  }
4922  else
4923  {
4924  $isImportant = false;
4925  }
4926  $this->parser->popState();
4927  $this->parser->appendToken(new CssAtFontFaceDeclarationToken($this->buffer, $value, $isImportant));
4928  $this->buffer = "";
4929  // --
4930  if ($char === "}")
4931  {
4932  $this->parser->appendToken(new CssAtFontFaceEndToken());
4933  $this->parser->popState();
4934  }
4935  }
4936  // End of @font-face at-rule block
4937  elseif ($char === "}" && $state === "T_AT_FONT_FACE")
4938  {
4939  $this->parser->appendToken(new CssAtFontFaceEndToken());
4940  $this->parser->clearBuffer();
4941  $this->parser->popState();
4942  }
4943  else
4944  {
4945  return false;
4946  }
4947  return true;
4948  }
4949  }
4950 
4951 /**
4952  * This {@link aCssToken CSS token} represents the end of a @font-face at-rule block.
4953  *
4954  * @package CssMin/Tokens
4955  * @link http://code.google.com/p/cssmin/
4956  * @author Joe Scylla <joe.scylla@gmail.com>
4957  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4958  * @license http://opensource.org/licenses/mit-license.php MIT License
4959  * @version 3.0.1
4960  */
4962  {
4963 
4964  }
4965 
4966 /**
4967  * This {@link aCssToken CSS token} represents a declaration of a @font-face at-rule block.
4968  *
4969  * @package CssMin/Tokens
4970  * @link http://code.google.com/p/cssmin/
4971  * @author Joe Scylla <joe.scylla@gmail.com>
4972  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4973  * @license http://opensource.org/licenses/mit-license.php MIT License
4974  * @version 3.0.1
4975  */
4977  {
4978 
4979  }
4980 
4981 /**
4982  * This {@link aCssToken CSS token} represents a @charset at-rule.
4983  *
4984  * @package CssMin/Tokens
4985  * @link http://code.google.com/p/cssmin/
4986  * @author Joe Scylla <joe.scylla@gmail.com>
4987  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
4988  * @license http://opensource.org/licenses/mit-license.php MIT License
4989  * @version 3.0.1
4990  */
4992  {
4993  /**
4994  * Charset of the @charset at-rule.
4995  *
4996  * @var string
4997  */
4998  public $Charset = "";
4999  /**
5000  * Set the properties of @charset at-rule token.
5001  *
5002  * @param string $charset Charset of the @charset at-rule token
5003  * @return void
5004  */
5005  public function __construct($charset)
5006  {
5007  $this->Charset = $charset;
5008  }
5009  /**
5010  * Implements {@link aCssToken::__toString()}.
5011  *
5012  * @return string
5013  */
5014  public function __toString()
5015  {
5016  return "@charset " . $this->Charset . ";";
5017  }
5018  }
5019 
5020 /**
5021  * {@link aCssParserPlugin Parser plugin} for parsing @charset at-rule.
5022  *
5023  * If a @charset at-rule was found this plugin will add a {@link CssAtCharsetToken} to the parser.
5024  *
5025  * @package CssMin/Parser/Plugins
5026  * @link http://code.google.com/p/cssmin/
5027  * @author Joe Scylla <joe.scylla@gmail.com>
5028  * @copyright 2008 - 2011 Joe Scylla <joe.scylla@gmail.com>
5029  * @license http://opensource.org/licenses/mit-license.php MIT License
5030  * @version 3.0.1
5031  */
5033  {
5034  /**
5035  * Implements {@link aCssParserPlugin::getTriggerChars()}.
5036  *
5037  * @return array
5038  */
5039  public function getTriggerChars()
5040  {
5041  return array("@", ";", "\n");
5042  }
5043  /**
5044  * Implements {@link aCssParserPlugin::getTriggerStates()}.
5045  *
5046  * @return array
5047  */
5048  public function getTriggerStates()
5049  {
5050  return array("T_DOCUMENT", "T_AT_CHARSET");
5051  }
5052  /**
5053  * Implements {@link aCssParserPlugin::parse()}.
5054  *
5055  * @param integer $index Current index
5056  * @param string $char Current char
5057  * @param string $previousChar Previous char
5058  * @return mixed TRUE will break the processing; FALSE continue with the next plugin; integer set a new index and break the processing
5059  */
5060  public function parse($index, $char, $previousChar, $state)
5061  {
5062  if ($char === "@" && $state === "T_DOCUMENT" && strtolower(substr($this->parser->getSource(), $index, 8)) === "@charset")
5063  {
5064  $this->parser->pushState("T_AT_CHARSET");
5065  $this->parser->clearBuffer();
5066  return $index + 8;
5067  }
5068  elseif (($char === ";" || $char === "\n") && $state === "T_AT_CHARSET")
5069  {
5070  $charset = $this->parser->getAndClearBuffer(";");
5071  $this->parser->popState();
5072  $this->parser->appendToken(new CssAtCharsetToken($charset));
5073  }
5074  else
5075  {
5076  return false;
5077  }
5078  return true;
5079  }
5080  }