Xataface  2.0alpha2
Xataface Application Framework
 All Data Structures Namespaces Files Functions Variables Groups Pages
CSSTool.php
Go to the documentation of this file.
1 <?php
3  private static $instance=null;
4  private $includePath = array();
5  private $stylesheets = array();
6  private $included = array();
7  private $ignoreFiles = array();
8 
9 
10  public function ignore($path){
11  $this->ignoreFiles[$path] = 1;
12  if ( isset($this->stylesheets[$path]) ){
13  unset($this->stylesheets[$path]);
14  }
15  }
16 
17 
18  public function isIgnored($path){
19  return @$this->ignoreFiles[$path];
20  }
21 
22 
23  public static function getInstance(){
24  if ( !isset(self::$instance) ){
25  self::$instance = new Dataface_CSSTool();
26  }
27  return self::$instance;
28  }
29 
30  public function getStylesheets(){
31  return $this->stylesheets;
32  }
33 
34  public function addPath($path, $url){
35  $this->includePath[$path] = $url;
36  }
37 
38  public function removePath($path){
39  unset($this->includePath[$path]);
40  }
41 
42  public function getPaths(){
43  return $this->includePath;
44  }
45 
46  public function import($path){
47  if ( @$this->ignoreFiles[$path] ) return;
48 
49  $this->stylesheets[$path] = 1;
50  }
51 
52 
53  public function getURL(){
54  $this->compile();
55  return DATAFACE_SITE_HREF.'?-action=css&--id='.$this->generateCacheKeyForScripts(array_keys($this->stylesheets));
56  }
57 
58  public function getIncluded(){
59  return $this->included;
60  }
61 
62  public function getContents(){
63  $this->compile();
64  return file_get_contents($this->getCSSCachePath(array_keys($this->stylesheets)));
65  }
66 
67  private function generateCacheKeyForScripts($stylesheets){
68  //$this->sortScripts();
69  $base = basename($stylesheets[0]);
70  //$base = basename($scripts[0]);
71  $base = substr($base, 0, 10);
72  return $base.'-'.md5(implode(PATH_SEPARATOR, $stylesheets));
73  }
74 
75  private function writeCSS($stylesheets, $contents){
76  $path = $this->getCSSCachePath($stylesheets);
77  return file_put_contents($path, $contents, LOCK_EX);
78  }
79 
80  private function getCSSCachePath($stylesheets){
81  return DATAFACE_SITE_PATH.'/templates_c/'.$this->generateCacheKeyForScripts($stylesheets).'.css';
82  }
83 
84  private function getManifestPath($stylesheets){
85  return DATAFACE_SITE_PATH.'/templates_c/'.$this->generateCacheKeyForScripts($stylesheets).'.manifest.css';
86  }
87 
88  private function writeManifest($stylesheets, $included){
89  $path = $this->getManifestPath($stylesheets);
90  return file_put_contents($path, json_encode($included), LOCK_EX);
91  }
92 
93  private function isCacheDirty($stylesheets){
94  $jspath = $this->getCSSCachePath($stylesheets);
95  $mfpath = $this->getManifestPath($stylesheets);
96 
97  if ( !file_exists($jspath) ) return true;
98  if ( !file_exists($mfpath) ) return true;
99  $mtime = filemtime($jspath);
100 
101  $deps = json_decode(file_get_contents($mfpath), true);
102  foreach ($deps as $script=>$file){
103  if ( filemtime($file) > $mtime ){
104  return true;
105  }
106  }
107  return false;
108 
109 
110  }
111 
112 
113 
114  public function compile($stylesheets=null, $clean=false){
115  if (!isset($stylesheets) ){
116  $stylesheets = array_keys($this->stylesheets);
117  }
118 
119  if ( $clean or $this->isCacheDirty($stylesheets) ){
120  $this->included = array();
121  $contents = $this->_compile($stylesheets);
122 
123  $contents = CssMin::minify($contents, array
124  (
125  "remove-empty-blocks" => true,
126  "remove-empty-rulesets" => true,
127  "remove-last-semicolons" => true,
128  "convert-css3-properties" => true,
129  "compress-color-values" => true,
130  "compress-unit-values" => true,
131  "emulate-css3-variables" => true
132  )
133  );
134 
135  file_put_contents($this->getCSSCachePath($stylesheets), $contents, LOCK_EX);
136  file_put_contents($this->getManifestPath($stylesheets), json_encode($this->included), LOCK_EX);
137  }
138 
139 
140  }
141 
156  private function _compile($stylesheets){
157  //$included = array();
158  $out=array();
159  if ( !is_array($stylesheets) ) $stylesheets = array($stylesheets);
160  $included =& $this->included;
161 
162  // Go through each script
163  foreach ($stylesheets as $script){
164  $contents = null;
165  if ( isset($included[$script]) ) continue;
166 
167  foreach ($this->includePath as $path=>$url){
168  $filepath = $path.DIRECTORY_SEPARATOR.$script;
169  $dirname = dirname($script);
170  if ( $dirname ) $url .= '/'.$dirname;
171  //echo "\nChecking $filepath\n";
172  if ( is_readable($filepath) ){
173  $contents = file_get_contents($filepath);
174  $contents = preg_replace('/url\(\s*[\'"]?\/?(.+?)[\'"]?\s*\)/i', 'url('.$url.'/$1)', $contents);
175  $included[$script] = $filepath;
176  break;
177  }
178  }
179 
180  if ( !isset($contents) ) throw new Exception(sprintf("Could not find script %s", $script));
181 
182 
183  $out[] = $contents;
184 
185 
186  }
187  return implode("\r\n", $out);
188  }
189 
190 
191 
192 
193  public function clearCache(){
194  $files = glob(DATAFACE_SITE_PATH.'/templates_c/*.css');
195  foreach($files as $f){
196  unlink($f);
197  }
198  $files = glob(DATAFACE_SITE_PATH.'/templates_c/*.manifest.css');
199  foreach($files as $f){
200  unlink($f);
201  }
202  }
203 
204 
205 }
206 
207 
226 class CssMin
227  {
233  const T_DOCUMENT = 1;
239  const T_COMMENT = 2;
245  const T_AT_RULE = 3;
251  const T_AT_MEDIA_START = 4;
257  const T_AT_MEDIA = 5;
263  const T_AT_MEDIA_END = 6;
275  const T_AT_FONT_FACE = 8;
287  const T_AT_FONT_FACE_END = 10;
293  const T_AT_PAGE_START = 11;
299  const T_AT_PAGE = 12;
305  const T_PAGE_DECLARATION = 13;
311  const T_AT_PAGE_END = 14;
317  const T_RULESET_START = 15;
323  const T_SELECTORS = 16;
335  const T_DECLARATIONS = 18;
341  const T_DECLARATION = 19;
347  const T_DECLARATIONS_END = 20;
353  const T_RULESET_END = 21;
359  const T_AT_VARIABLES_START = 100;
365  const T_AT_VARIABLES = 101;
377  const T_AT_VARIABLES_END = 103;
383  const T_STRING = 254;
389  const T_STRING_URL = 255;
395  private static $transformations = array
396  (
397  "border-radius" => array("-moz-border-radius", "-webkit-border-radius", "-khtml-border-radius"),
398  "border-top-left-radius" => array("-moz-border-radius-topleft", "-webkit-border-top-left-radius", "-khtml-top-left-radius"),
399  "border-top-right-radius" => array("-moz-border-radius-topright", "-webkit-border-top-right-radius", "-khtml-top-right-radius"),
400  "border-bottom-right-radius" => array("-moz-border-radius-bottomright", "-webkit-border-bottom-right-radius", "-khtml-border-bottom-right-radius"),
401  "border-bottom-left-radius" => array("-moz-border-radius-bottomleft", "-webkit-border-bottom-left-radius", "-khtml-border-bottom-left-radius"),
402  "box-shadow" => array("-moz-box-shadow", "-webkit-box-shadow", "-khtml-box-shadow"),
403  "opacity" => array(array("CssMin", "_tOpacity")),
404  "text-shadow" => array("-moz-text-shadow", "-webkit-text-shadow", "-khtml-text-shadow"),
405  "white-space" => array(array("CssMin", "_tWhiteSpacePreWrap"))
406  );
414  public static function minify($css, $config = array())
415  {
416  $tokens = self::parse($css);
417  $config = array_merge(array
418  (
419  "remove-empty-blocks" => true,
420  "remove-empty-rulesets" => true,
421  "remove-last-semicolons" => true,
422  "convert-css3-properties" => false,
423  "convert-color-values" => false,
424  "compress-color-values" => false,
425  "compress-unit-values" => false,
426  "emulate-css3-variables" => true,
427  ), $config);
428  // Minification options
429  $sRemoveEmptyBlocks = $config["remove-empty-blocks"];
430  $sRemoveEmptyRulesets = $config["remove-empty-rulesets"];
431  $sRemoveLastSemicolon = $config["remove-last-semicolons"];
432  $sConvertCss3Properties = $config["convert-css3-properties"];
433  $sCompressUnitValues = $config["compress-unit-values"];
434  $sConvertColorValues = $config["convert-color-values"];
435  $sCompressColorValues = $config["compress-color-values"];
436  $sEmulateCcss3Variables = $config["emulate-css3-variables"];
437  $sRemoveTokens = array(self::T_COMMENT);
438  // Remove tokens
439  if (!$sEmulateCcss3Variables)
440  {
441  $sRemoveTokens = array_merge($sRemoveTokens, array(self::T_AT_VARIABLES_START, self::T_VARIABLE_DECLARATION, self::T_AT_VARIABLES_END));
442  }
443  for($i = 0, $l = count($tokens); $i < $l; $i++)
444  {
445  if (in_array($tokens[$i][0], $sRemoveTokens))
446  {
447  unset($tokens[$i]);
448  }
449  }
450  $tokens = array_values($tokens);
451  // Remove empty rulesets
452  if ($sRemoveEmptyRulesets)
453  {
454  for($i = 0, $l = count($tokens); $i < $l; $i++)
455  {
456  // Remove empty rulesets
457  if ($tokens[$i][0] == self::T_RULESET_START && $tokens[$i+4][0] == self::T_RULESET_END)
458  {
459  unset($tokens[$i]); // T_RULESET_START
460  unset($tokens[++$i]); // T_SELECTORS
461  unset($tokens[++$i]); // T_DECLARATIONS_START
462  unset($tokens[++$i]); // T_DECLARATIONS_END
463  unset($tokens[++$i]); // T_RULESET_END
464  }
465  }
466  $tokens = array_values($tokens);
467  }
468  // Remove empty @media, @font-face or @page blocks
469  if ($sRemoveEmptyBlocks)
470  {
471  for($i = 0, $l = count($tokens); $i < $l; $i++)
472  {
473  // Remove empty @media, @font-face or @page blocks
474  if (($tokens[$i][0] == self::T_AT_MEDIA_START && $tokens[$i+1][0] == self::T_AT_MEDIA_END)
475  || ($tokens[$i][0] == self::T_AT_FONT_FACE_START && $tokens[$i+1][0] == self::T_AT_FONT_FACE_END)
476  || ($tokens[$i][0] == self::T_AT_PAGE_START && $tokens[$i+1][0] == self::T_AT_PAGE_END))
477  {
478  unset($tokens[$i]); // T_AT_MEDIA_START, T_AT_FONT_FACE_START, T_AT_PAGE_START
479  unset($tokens[++$i]); // T_AT_MEDIA_END, T_AT_FONT_FACE_END, T_AT_PAGE_END
480  }
481  }
482  $tokens = array_values($tokens);
483  }
484  // CSS Level 3 variables: parse variables
485  if ($sEmulateCcss3Variables)
486  {
487  // Parse variables
488  $variables = array();
489  for($i = 0, $l = count($tokens); $i < $l; $i++)
490  {
491  if ($tokens[$i][0] == self::T_VARIABLE_DECLARATION)
492  {
493  for($i2 = 0, $l2 = count($tokens[$i][3]); $i2 < $l2; $i2++)
494  {
495  if (!isset($variables[$tokens[$i][3][$i2]]))
496  {
497  $variables[$tokens[$i][3][$i2]] = array();
498  }
499  $variables[$tokens[$i][3][$i2]][$tokens[$i][1]] = $tokens[$i][2];
500  }
501  }
502  }
503  }
504  // Conversion and compression
505  for($i = 0, $l = count($tokens); $i < $l; $i++)
506  {
507  if ($tokens[$i][0] == self::T_DECLARATION)
508  {
509  // CSS Level 3 variables
510  if ($sEmulateCcss3Variables)
511  {
512  if (substr($tokens[$i][2], 0, 4) == "var(" && substr($tokens[$i][2], -1, 1) == ")")
513  {
514  $tokens[$i][3][] = "all";
515  $variable = trim(substr($tokens[$i][2], 4, -1));
516  for($i2 = 0, $l2 = count($tokens[$i][3]); $i2 < $l2; $i2++)
517  {
518  if (isset($variables[$tokens[$i][3][$i2]][$variable]))
519  {
520  $tokens[$i][2] = $variables[$tokens[$i][3][$i2]][$variable];
521  break;
522  }
523  }
524  }
525  }
526  // Compress unit values
527  if ($sCompressUnitValues)
528  {
529  // Compress "0.5px" to ".5px"
530  $tokens[$i][2] = preg_replace("/(^| |-)0\.([0-9]+)(%|em|ex|px|in|cm|mm|pt|pc)/iS", "\${1}.\${2}\${3}", $tokens[$i][2]);
531  // Compress "0px" to "0"
532  $tokens[$i][2] = preg_replace("/(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/iS", "\${1}0", $tokens[$i][2]);
533  // Compress "0 0 0 0" to "0"
534  if ($tokens[$i][2] == "0 0 0 0") {$tokens[$i][2] = "0";}
535  }
536  // Convert RGB color values to hex ("rgb(200,60%,5)" => "#c89905")
537  if ($sConvertColorValues && preg_match("/rgb\s*\(\s*([0-9%]+)\s*,\s*([0-9%]+)\s*,\s*([0-9%]+)\s*\)/iS", $tokens[$i][2], $m))
538  {
539  for ($i2 = 1, $l2 = count($m); $i2 < $l2; $i2++)
540  {
541  if (strpos("%", $m[$i2]) !== false)
542  {
543  $m[$i2] = substr($m[$i2], 0, -1);
544  $m[$i2] = (int) (256 * ($m[$i2] / 100));
545  }
546  $m[$i2] = str_pad(dechex($m[$i2]), 2, "0", STR_PAD_LEFT);
547  }
548  $tokens[$i][2] = str_replace($m[0], "#" . $m[1] . $m[2] . $m[3], $tokens[$i][2]);
549  }
550  // Compress color values ("#aabbcc" to "#abc")
551  if ($sCompressColorValues && preg_match("/\#([0-9a-f]{6})/iS", $tokens[$i][2], $m))
552  {
553  $m[1] = strtolower($m[1]);
554  if (substr($m[1], 0, 1) == substr($m[1], 1, 1) && substr($m[1], 2, 1) == substr($m[1], 3, 1) && substr($m[1], 4, 1) == substr($m[1], 5, 1))
555  {
556  $tokens[$i][2] = str_replace($m[0], "#" . substr($m[1], 0, 1) . substr($m[1], 2, 1) . substr($m[1], 4, 1), $tokens[$i][2]);
557  }
558  }
559  }
560  }
561  // Create minified css
562  $r = "";
563  for($i = 0, $l = count($tokens); $i < $l; $i++)
564  {
565  // T_AT_RULE
566  if ($tokens[$i][0] == self::T_AT_RULE)
567  {
568  $r .= "@" . $tokens[$i][1] . " " . $tokens[$i][2] . ";";
569  }
570  // T_AT_MEDIA_START
571  elseif ($tokens[$i][0] == self::T_AT_MEDIA_START)
572  {
573  if (count($tokens[$i][1]) == 1 && $tokens[$i][1][0] == "all")
574  {
575  $r .= "@media{";
576  }
577  else
578  {
579  $r .= "@media " . implode(",", $tokens[$i][1]) . "{";
580  }
581  }
582  // T_AT_FONT_FACE_START
583  elseif ($tokens[$i][0] == self::T_AT_FONT_FACE_START)
584  {
585  $r .= "@font-face{";
586  }
587  // T_FONT_FACE_DECLARATION
588  elseif ($tokens[$i][0] == self::T_FONT_FACE_DECLARATION)
589  {
590  $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_AT_FONT_FACE_END ? "" : ";");
591  }
592  // T_AT_PAGE_START
593  elseif ($tokens[$i][0] == self::T_AT_PAGE_START)
594  {
595  $r .= "@page{";
596  }
597  // T_PAGE_DECLARATION
598  elseif ($tokens[$i][0] == self::T_PAGE_DECLARATION)
599  {
600  $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_AT_PAGE_END ? "" : ";");
601  }
602  // T_SELECTORS
603  elseif ($tokens[$i][0] == self::T_SELECTORS)
604  {
605  $r .= implode(",", $tokens[$i][1]);
606  }
607  // Start of declarations
608  elseif ($tokens[$i][0] == self::T_DECLARATIONS_START)
609  {
610  $r .= "{";
611  }
612  // T_DECLARATION
613  elseif ($tokens[$i][0] == self::T_DECLARATION)
614  {
615  if ($sConvertCss3Properties && isset(self::$transformations[$tokens[$i][1]]))
616  {
617  foreach (self::$transformations[$tokens[$i][1]] as $value)
618  {
619  if (!is_array($value))
620  {
621  $r .= $value . ":" . $tokens[$i][2] . ";";
622  }
623  elseif (is_array($value) && is_callable($value))
624  {
625  $r.= call_user_func_array($value, array($tokens[$i][1], $tokens[$i][2]));
626 
627  }
628  }
629  }
630  $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_DECLARATIONS_END ? "" : ";");
631  }
632  // T_DECLARATIONS_END, T_AT_MEDIA_END, T_AT_FONT_FACE_END, T_AT_PAGE_END
633  elseif (in_array($tokens[$i][0], array(self::T_DECLARATIONS_END, self::T_AT_MEDIA_END, self::T_AT_FONT_FACE_END, self::T_AT_PAGE_END)))
634  {
635  $r .= "}";
636  }
637  else
638  {
639  // Tokens with no output:
640  // T_COMMENT
641  // T_RULESET_START
642  // T_RULESET_END
643  // T_AT_VARIABLES_START
644  // T_VARIABLE_DECLARATION
645  // T_AT_VARIABLES_END
646  }
647  }
648  return $r;
649  }
656  public static function parse($css)
657  {
658  // Settings
659  $sDefaultScope = array("all"); // Default scope
660  $sDefaultTrim = " \t\n\r\0\x0B"; // Default trim charlist
661  $sTokenChars = "@{}();:\n\"'/*,"; // Tokens triggering parser processing
662  // Basic variables
663  $c = null; // Current char
664  $p = null; // Previous char
665  $buffer = ""; // Buffer
666  $state = array(self::T_DOCUMENT); // State stack
667  $currentState = self::T_DOCUMENT; // Current state
668  $scope = $sDefaultScope; // Current scope
669  $stringChar = null; // String delimiter char
670  $isFilterWs = true; // Filter double whitespaces?
671  $selectors = array(); // Array with collected selectors
672  $r = array(); // Return value
673  // Prepare css
674  $css = str_replace("\r\n", "\n", $css); // Windows to Unix line endings
675  $css = str_replace("\r", "\n", $css); // Mac to Unix line endings
676  while (strpos($css, "\n\n") !== false)
677  {
678  $css = str_replace("\n\n", "\n", $css); // Remove double line endings
679  }
680  $css = str_replace("\t", " ", $css); // Convert tabs to spaces
681  // Parse css
682  for ($i = 0, $l = strlen($css); $i < $l; $i++)
683  {
684  $c = substr($css, $i, 1);
685  // Filter out double spaces
686  if ($isFilterWs && $c == " " && $c == $p)
687  {
688  continue;
689  }
690  $buffer .= $c;
691  if (strpos($sTokenChars, $c) !== false)
692  {
693  //
694  $currentState = $state[count($state) - 1];
695  /*
696  * Start of comment
697  */
698  if ($p == "/" && $c == "*" && $currentState != self::T_STRING && $currentState != self::T_COMMENT)
699  {
700  $saveBuffer = substr($buffer, 0, -2); // save the buffer (will get restored with comment ending)
701  $buffer = $c;
702  $isFilterWs = false;
703  array_push($state, self::T_COMMENT);
704  }
705  /*
706  * End of comment
707  */
708  elseif ($p == "*" && $c == "/" && $currentState == self::T_COMMENT)
709  {
710  $r[] = array(self::T_COMMENT, trim($buffer));
711  $buffer = $saveBuffer;
712  $isFilterWs = true;
713  array_pop($state);
714  }
715  /*
716  * Start of string
717  */
718  elseif (($c == "\"" || $c == "'") && $currentState != self::T_STRING && $currentState != self::T_COMMENT && $currentState != self::T_STRING_URL)
719  {
720  $stringChar = $c;
721  $isFilterWs = false;
722  array_push($state, self::T_STRING);
723  }
727  elseif ($c == "\n" && $p == "\\" && $currentState == self::T_STRING)
728  {
729  $buffer = substr($buffer, 0, -2);
730  }
731  /*
732  * End of string
733  */
734  elseif ($c === $stringChar && $currentState == self::T_STRING)
735  {
736  if ($p == "\\") // Previous char is a escape char
737  {
738  $count = 1;
739  $i2 = $i -2;
740  while (substr($css, $i2, 1) == "\\")
741  {
742  $count++;
743  $i2--;
744  }
745  // if count of escape chars is uneven => continue with string...
746  if ($count % 2)
747  {
748  continue;
749  }
750  }
751  // ...else end the string
752  $isFilterWs = true;
753  array_pop($state);
754  $stringChar = null;
755  }
759  elseif ($c == "(" && ($currentState != self::T_COMMENT && $currentState != self::T_STRING) && strtolower(substr($css, $i - 3, 3) == "url")
760  && ($currentState == self::T_DECLARATION || $currentState == self::T_FONT_FACE_DECLARATION || $currentState == self::T_PAGE_DECLARATION || $currentState == self::T_VARIABLE_DECLARATION))
761  {
762  array_push($state, self::T_STRING_URL);
763  }
767  elseif (($c == ")" || $c == "\n") && ($currentState != self::T_COMMENT && $currentState != self::T_STRING) && $currentState == self::T_STRING_URL)
768  {
769  if ($p == "\\")
770  {
771  continue;
772  }
773  array_pop($state);
774  }
775  /*
776  * Start of at-rule @media block
777  */
778  elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 6)) == "@media")
779  {
780  $i = $i + 6;
781  $buffer = "";
782  array_push($state, self::T_AT_MEDIA_START);
783  }
784  /*
785  * At-rule @media block media types
786  */
787  elseif ($c == "{" && $currentState == self::T_AT_MEDIA_START)
788  {
789  $buffer = strtolower(trim($buffer, $sDefaultTrim . "{"));
790  $scope = $buffer != "" ? array_filter(array_map("trim", explode(",", $buffer))) : $sDefaultScope;
791  $r[] = array(self::T_AT_MEDIA_START, $scope);
792  $i = $i++;
793  $buffer = "";
794  array_pop($state);
795  array_push($state, self::T_AT_MEDIA);
796  }
797  /*
798  * End of at-rule @media block
799  */
800  elseif ($currentState == self::T_AT_MEDIA && $c == "}")
801  {
802  $r[] = array(self::T_AT_MEDIA_END);
803  $scope = $sDefaultScope;
804  $buffer = "";
805  array_pop($state);
806  }
807  /*
808  * Start of at-rule @font-face block
809  */
810  elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 10)) == "@font-face")
811  {
812  $r[] = array(self::T_AT_FONT_FACE_START);
813  $i = $i + 10;
814  $buffer = "";
815  array_push($state, self::T_AT_FONT_FACE);
816  }
817  /*
818  * @font-face declaration: Property
819  */
820  elseif ($c == ":" && $currentState == self::T_AT_FONT_FACE)
821  {
822  $property = trim($buffer, $sDefaultTrim . ":{");
823  $buffer = "";
824  array_push($state, self::T_FONT_FACE_DECLARATION);
825  }
826  /*
827  * @font-face declaration: Value
828  */
829  elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_FONT_FACE_DECLARATION)
830  {
831  $value = trim($buffer, $sDefaultTrim . ";}");
832  $r[] = array(self::T_FONT_FACE_DECLARATION, $property, $value, $scope);
833  $buffer = "";
834  array_pop($state);
835  if ($c == "}") // @font-face declaration closed with a right curly brace => closes @font-face block
836  {
837  array_pop($state);
838  $r[] = array(self::T_AT_FONT_FACE_END);
839  }
840  }
841  /*
842  * End of at-rule @font-face block
843  */
844  elseif ($c == "}" && $currentState == self::T_AT_FONT_FACE)
845  {
846  $r[] = array(self::T_AT_FONT_FACE_END);
847  $buffer = "";
848  array_pop($state);
849  }
850  /*
851  * Start of at-rule @page block
852  */
853  elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 5)) == "@page")
854  {
855  $r[] = array(self::T_AT_PAGE_START);
856  $i = $i + 5;
857  $buffer = "";
858  array_push($state, self::T_AT_PAGE);
859  }
860  /*
861  * @page declaration: Property
862  */
863  elseif ($c == ":" && $currentState == self::T_AT_PAGE)
864  {
865  $property = trim($buffer, $sDefaultTrim . ":{");
866  $buffer = "";
867  array_push($state, self::T_PAGE_DECLARATION);
868  }
869  /*
870  * @page declaration: Value
871  */
872  elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_PAGE_DECLARATION)
873  {
874  $value = trim($buffer, $sDefaultTrim . ";}");
875  $r[] = array(self::T_PAGE_DECLARATION, $property, $value, $scope);
876  $buffer = "";
877  array_pop($state);
878  if ($c == "}") // @page declaration closed with a right curly brace => closes @font-face block
879  {
880  array_pop($state);
881  $r[] = array(self::T_AT_PAGE_END);
882  }
883  }
884  /*
885  * End of at-rule @page block
886  */
887  elseif ($c == "}" && $currentState == self::T_AT_PAGE)
888  {
889  $r[] = array(self::T_AT_PAGE_END);
890  $buffer = "";
891  array_pop($state);
892  }
893  /*
894  * Start of at-rule @variables block
895  */
896  elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 10)) == "@variables")
897  {
898  $i = $i + 10;
899  $buffer = "";
900  array_push($state, self::T_AT_VARIABLES_START);
901  }
902  /*
903  * @variables media types
904  */
905  elseif ($c == "{" && $currentState == self::T_AT_VARIABLES_START)
906  {
907  $buffer = strtolower(trim($buffer, $sDefaultTrim . "{"));
908  $r[] = array(self::T_AT_VARIABLES_START, $scope);
909  $scope = $buffer != "" ? array_filter(array_map("trim", explode(",", $buffer))) : $sDefaultScope;
910  $i = $i++;
911  $buffer = "";
912  array_pop($state);
913  array_push($state, self::T_AT_VARIABLES);
914  }
915  /*
916  * @variables declaration: Property
917  */
918  elseif ($c == ":" && $currentState == self::T_AT_VARIABLES)
919  {
920  $property = trim($buffer, $sDefaultTrim . ":");
921  $buffer = "";
922  array_push($state, self::T_VARIABLE_DECLARATION);
923  }
924  /*
925  * @variables declaration: Value
926  */
927  elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_VARIABLE_DECLARATION)
928  {
929  $value = trim($buffer, $sDefaultTrim . ";}");
930  $r[] = array(self::T_VARIABLE_DECLARATION, $property, $value, $scope);
931  $buffer = "";
932  array_pop($state);
933  if ($c == "}") // @variable declaration closed with a right curly brace => closes @variables block
934  {
935  array_pop($state);
936  $r[] = array(self::T_AT_VARIABLES_END);
937  $scope = $sDefaultScope;
938  }
939  }
940  /*
941  * End of at-rule @variables block
942  */
943  elseif ($c == "}" && $currentState == self::T_AT_VARIABLES)
944  {
945  $r[] = array(self::T_AT_VARIABLES_END);
946  $scope = $sDefaultScope;
947  $buffer = "";
948  array_pop($state);
949  }
950  /*
951  * Start of document level at-rule
952  */
953  elseif ($c == "@" && $currentState == self::T_DOCUMENT)
954  {
955  $buffer = "";
956  array_push($state, self::T_AT_RULE);
957  }
958  /*
959  * End of document level at-rule
960  */
961  elseif ($c == ";" && $currentState == self::T_AT_RULE)
962  {
963  $pos = strpos($buffer, " ");
964  $rule = substr($buffer, 0, $pos);
965  $value = trim(substr($buffer, $pos), $sDefaultTrim . ";");
966  $r[] = array(self::T_AT_RULE, $rule, $value);
967  $buffer = "";
968  array_pop($state);
969  }
973  elseif ($c == "," && ($currentState == self::T_AT_MEDIA || $currentState == self::T_DOCUMENT))
974  {
975  $selectors[]= trim($buffer, $sDefaultTrim . ",");
976  $buffer = "";
977  }
978  /*
979  * Start of ruleset
980  */
981  elseif ($c == "{" && ($currentState == self::T_AT_MEDIA || $currentState == self::T_DOCUMENT))
982  {
983  $selectors[]= trim($buffer, $sDefaultTrim . "{");
984  $selectors = array_filter(array_map("trim", $selectors));
985  $r[] = array(self::T_RULESET_START);
986  $r[] = array(self::T_SELECTORS, $selectors);
987  $r[] = array(self::T_DECLARATIONS_START);
988  $buffer = "";
989  $selectors = array();
990  array_push($state, self::T_DECLARATIONS);
991  }
992  /*
993  * Declaration: Property
994  */
995  elseif ($c == ":" && $currentState == self::T_DECLARATIONS)
996  {
997  $property = trim($buffer, $sDefaultTrim . ":;");
998  $buffer = "";
999  array_push($state, self::T_DECLARATION);
1000  }
1001  /*
1002  * Declaration: Value
1003  */
1004  elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_DECLARATION)
1005  {
1006  $value = trim($buffer, $sDefaultTrim . ";}");
1007  $r[] = array(self::T_DECLARATION, $property, $value, $scope);
1008  $buffer = "";
1009  array_pop($state);
1010  if ($c == "}") // declaration closed with a right curly brace => close ruleset
1011  {
1012  array_pop($state);
1013  $r[] = array(self::T_DECLARATIONS_END);
1014  $r[] = array(self::T_RULESET_END);
1015  }
1016  }
1017  /*
1018  * End of ruleset
1019  */
1020  elseif ($c == "}" && $currentState == self::T_DECLARATIONS)
1021  {
1022  $r[] = array(self::T_DECLARATIONS_END);
1023  $r[] = array(self::T_RULESET_END);
1024  $buffer = "";
1025  array_pop($state);
1026  }
1027 
1028  }
1029 
1030  $p = $c;
1031  }
1032  return $r;
1033  }
1041  private static function _tOpacity($property, $value)
1042  {
1043  $ieValue = (int) ((float) $value * 100);
1044  $r = "-moz-opacity:" . $value . ";"; // Firefox < 3.5
1045  $r .= "-ms-filter: \"alpha(opacity=" . $ieValue . ")\";"; // Internet Explorer 8
1046  $r .= "filter: alpha(opacity=" . $ieValue . ");zoom: 1;"; // Internet Explorer 4 - 7
1047  return $r;
1048  }
1056  private static function _tWhiteSpacePreWrap($property, $value)
1057  {
1058  if (strtolower($value) == "pre-wrap")
1059  {
1060  $r = "white-space:-moz-pre-wrap;"; // Mozilla
1061  $r .= "white-space:-webkit-pre-wrap;"; // Webkit
1062  $r .= "white-space:-khtml-pre-wrap;"; // khtml
1063  $r .= "white-space:-pre-wrap;"; // Opera 4 - 6
1064  $r .= "white-space:-o-pre-wrap;"; // Opera 7+
1065  $r .= "word-wrap:break-word;"; // Internet Explorer 5.5+
1066  return $r;
1067  }
1068  else
1069  {
1070  return "";
1071  }
1072  }
1073  }
1074 ?>