![]() |
Xataface 2.0
Xataface Application Framework
|
00001 <?php 00002 class Dataface_CSSTool { 00003 private static $instance=null; 00004 private $includePath = array(); 00005 private $stylesheets = array(); 00006 private $included = array(); 00007 private $ignoreFiles = array(); 00008 00009 00010 public function ignore($path){ 00011 $this->ignoreFiles[$path] = 1; 00012 if ( isset($this->stylesheets[$path]) ){ 00013 unset($this->stylesheets[$path]); 00014 } 00015 } 00016 00017 00018 public function isIgnored($path){ 00019 return @$this->ignoreFiles[$path]; 00020 } 00021 00022 00023 public static function getInstance(){ 00024 if ( !isset(self::$instance) ){ 00025 self::$instance = new Dataface_CSSTool(); 00026 } 00027 return self::$instance; 00028 } 00029 00030 public function getStylesheets(){ 00031 return $this->stylesheets; 00032 } 00033 00034 public function addPath($path, $url){ 00035 $this->includePath[$path] = $url; 00036 } 00037 00038 public function removePath($path){ 00039 unset($this->includePath[$path]); 00040 } 00041 00042 public function getPaths(){ 00043 return $this->includePath; 00044 } 00045 00046 public function import($path){ 00047 if ( @$this->ignoreFiles[$path] ) return; 00048 00049 $this->stylesheets[$path] = 1; 00050 } 00051 00052 00053 public function getURL(){ 00054 $this->compile(); 00055 return DATAFACE_SITE_HREF.'?-action=css&--id='.$this->generateCacheKeyForScripts(array_keys($this->stylesheets)); 00056 } 00057 00058 public function getIncluded(){ 00059 return $this->included; 00060 } 00061 00062 public function getContents(){ 00063 $this->compile(); 00064 return file_get_contents($this->getCSSCachePath(array_keys($this->stylesheets))); 00065 } 00066 00067 private function generateCacheKeyForScripts($stylesheets){ 00068 //$this->sortScripts(); 00069 $base = basename($stylesheets[0]); 00070 //$base = basename($scripts[0]); 00071 $base = substr($base, 0, 10); 00072 return $base.'-'.md5(implode(PATH_SEPARATOR, $stylesheets)); 00073 } 00074 00075 private function writeCSS($stylesheets, $contents){ 00076 $path = $this->getCSSCachePath($stylesheets); 00077 return file_put_contents($path, $contents, LOCK_EX); 00078 } 00079 00080 private function getCSSCachePath($stylesheets){ 00081 return DATAFACE_SITE_PATH.'/templates_c/'.$this->generateCacheKeyForScripts($stylesheets).'.css'; 00082 } 00083 00084 private function getManifestPath($stylesheets){ 00085 return DATAFACE_SITE_PATH.'/templates_c/'.$this->generateCacheKeyForScripts($stylesheets).'.manifest.css'; 00086 } 00087 00088 private function writeManifest($stylesheets, $included){ 00089 $path = $this->getManifestPath($stylesheets); 00090 return file_put_contents($path, json_encode($included), LOCK_EX); 00091 } 00092 00093 private function isCacheDirty($stylesheets){ 00094 $jspath = $this->getCSSCachePath($stylesheets); 00095 $mfpath = $this->getManifestPath($stylesheets); 00096 00097 if ( !file_exists($jspath) ) return true; 00098 if ( !file_exists($mfpath) ) return true; 00099 $mtime = filemtime($jspath); 00100 00101 $deps = json_decode(file_get_contents($mfpath), true); 00102 foreach ($deps as $script=>$file){ 00103 if ( filemtime($file) > $mtime ){ 00104 return true; 00105 } 00106 } 00107 return false; 00108 00109 00110 } 00111 00112 00113 00114 public function compile($stylesheets=null, $clean=false){ 00115 if (!isset($stylesheets) ){ 00116 $stylesheets = array_keys($this->stylesheets); 00117 } 00118 00119 if ( $clean or $this->isCacheDirty($stylesheets) ){ 00120 $this->included = array(); 00121 $contents = $this->_compile($stylesheets); 00122 00123 $contents = CssMin::minify($contents, array 00124 ( 00125 "remove-empty-blocks" => true, 00126 "remove-empty-rulesets" => true, 00127 "remove-last-semicolons" => true, 00128 "convert-css3-properties" => true, 00129 "compress-color-values" => true, 00130 "compress-unit-values" => true, 00131 "emulate-css3-variables" => true 00132 ) 00133 ); 00134 00135 file_put_contents($this->getCSSCachePath($stylesheets), $contents, LOCK_EX); 00136 file_put_contents($this->getManifestPath($stylesheets), json_encode($this->included), LOCK_EX); 00137 } 00138 00139 00140 } 00141 00156 private function _compile($stylesheets){ 00157 //$included = array(); 00158 $out=array(); 00159 if ( !is_array($stylesheets) ) $stylesheets = array($stylesheets); 00160 $included =& $this->included; 00161 00162 // Go through each script 00163 foreach ($stylesheets as $script){ 00164 $contents = null; 00165 if ( isset($included[$script]) ) continue; 00166 00167 foreach ($this->includePath as $path=>$url){ 00168 $filepath = $path.DIRECTORY_SEPARATOR.$script; 00169 $dirname = dirname($script); 00170 if ( $dirname ) $url .= '/'.$dirname; 00171 //echo "\nChecking $filepath\n"; 00172 if ( is_readable($filepath) ){ 00173 $contents = file_get_contents($filepath); 00174 $contents = preg_replace('/url\(\s*[\'"]?\/?(.+?)[\'"]?\s*\)/i', 'url('.$url.'/$1)', $contents); 00175 $included[$script] = $filepath; 00176 break; 00177 } 00178 } 00179 00180 if ( !isset($contents) ) throw new Exception(sprintf("Could not find script %s", $script)); 00181 00182 00183 $out[] = $contents; 00184 00185 00186 } 00187 return implode("\r\n", $out); 00188 } 00189 00190 00191 00192 00193 public function clearCache(){ 00194 $files = glob(DATAFACE_SITE_PATH.'/templates_c/*.css'); 00195 foreach($files as $f){ 00196 unlink($f); 00197 } 00198 $files = glob(DATAFACE_SITE_PATH.'/templates_c/*.manifest.css'); 00199 foreach($files as $f){ 00200 unlink($f); 00201 } 00202 } 00203 00204 00205 } 00206 00207 00226 class CssMin 00227 { 00233 const T_DOCUMENT = 1; 00239 const T_COMMENT = 2; 00245 const T_AT_RULE = 3; 00251 const T_AT_MEDIA_START = 4; 00257 const T_AT_MEDIA = 5; 00263 const T_AT_MEDIA_END = 6; 00269 const T_AT_FONT_FACE_START = 7; 00275 const T_AT_FONT_FACE = 8; 00281 const T_FONT_FACE_DECLARATION = 9; 00287 const T_AT_FONT_FACE_END = 10; 00293 const T_AT_PAGE_START = 11; 00299 const T_AT_PAGE = 12; 00305 const T_PAGE_DECLARATION = 13; 00311 const T_AT_PAGE_END = 14; 00317 const T_RULESET_START = 15; 00323 const T_SELECTORS = 16; 00329 const T_DECLARATIONS_START = 17; 00335 const T_DECLARATIONS = 18; 00341 const T_DECLARATION = 19; 00347 const T_DECLARATIONS_END = 20; 00353 const T_RULESET_END = 21; 00359 const T_AT_VARIABLES_START = 100; 00365 const T_AT_VARIABLES = 101; 00371 const T_VARIABLE_DECLARATION = 102; 00377 const T_AT_VARIABLES_END = 103; 00383 const T_STRING = 254; 00389 const T_STRING_URL = 255; 00395 private static $transformations = array 00396 ( 00397 "border-radius" => array("-moz-border-radius", "-webkit-border-radius", "-khtml-border-radius"), 00398 "border-top-left-radius" => array("-moz-border-radius-topleft", "-webkit-border-top-left-radius", "-khtml-top-left-radius"), 00399 "border-top-right-radius" => array("-moz-border-radius-topright", "-webkit-border-top-right-radius", "-khtml-top-right-radius"), 00400 "border-bottom-right-radius" => array("-moz-border-radius-bottomright", "-webkit-border-bottom-right-radius", "-khtml-border-bottom-right-radius"), 00401 "border-bottom-left-radius" => array("-moz-border-radius-bottomleft", "-webkit-border-bottom-left-radius", "-khtml-border-bottom-left-radius"), 00402 "box-shadow" => array("-moz-box-shadow", "-webkit-box-shadow", "-khtml-box-shadow"), 00403 "opacity" => array(array("CssMin", "_tOpacity")), 00404 "text-shadow" => array("-moz-text-shadow", "-webkit-text-shadow", "-khtml-text-shadow"), 00405 "white-space" => array(array("CssMin", "_tWhiteSpacePreWrap")) 00406 ); 00414 public static function minify($css, $config = array()) 00415 { 00416 $tokens = self::parse($css); 00417 $config = array_merge(array 00418 ( 00419 "remove-empty-blocks" => true, 00420 "remove-empty-rulesets" => true, 00421 "remove-last-semicolons" => true, 00422 "convert-css3-properties" => false, 00423 "convert-color-values" => false, 00424 "compress-color-values" => false, 00425 "compress-unit-values" => false, 00426 "emulate-css3-variables" => true, 00427 ), $config); 00428 // Minification options 00429 $sRemoveEmptyBlocks = $config["remove-empty-blocks"]; 00430 $sRemoveEmptyRulesets = $config["remove-empty-rulesets"]; 00431 $sRemoveLastSemicolon = $config["remove-last-semicolons"]; 00432 $sConvertCss3Properties = $config["convert-css3-properties"]; 00433 $sCompressUnitValues = $config["compress-unit-values"]; 00434 $sConvertColorValues = $config["convert-color-values"]; 00435 $sCompressColorValues = $config["compress-color-values"]; 00436 $sEmulateCcss3Variables = $config["emulate-css3-variables"]; 00437 $sRemoveTokens = array(self::T_COMMENT); 00438 // Remove tokens 00439 if (!$sEmulateCcss3Variables) 00440 { 00441 $sRemoveTokens = array_merge($sRemoveTokens, array(self::T_AT_VARIABLES_START, self::T_VARIABLE_DECLARATION, self::T_AT_VARIABLES_END)); 00442 } 00443 for($i = 0, $l = count($tokens); $i < $l; $i++) 00444 { 00445 if (in_array($tokens[$i][0], $sRemoveTokens)) 00446 { 00447 unset($tokens[$i]); 00448 } 00449 } 00450 $tokens = array_values($tokens); 00451 // Remove empty rulesets 00452 if ($sRemoveEmptyRulesets) 00453 { 00454 for($i = 0, $l = count($tokens); $i < $l; $i++) 00455 { 00456 // Remove empty rulesets 00457 if ($tokens[$i][0] == self::T_RULESET_START && $tokens[$i+4][0] == self::T_RULESET_END) 00458 { 00459 unset($tokens[$i]); // T_RULESET_START 00460 unset($tokens[++$i]); // T_SELECTORS 00461 unset($tokens[++$i]); // T_DECLARATIONS_START 00462 unset($tokens[++$i]); // T_DECLARATIONS_END 00463 unset($tokens[++$i]); // T_RULESET_END 00464 } 00465 } 00466 $tokens = array_values($tokens); 00467 } 00468 // Remove empty @media, @font-face or @page blocks 00469 if ($sRemoveEmptyBlocks) 00470 { 00471 for($i = 0, $l = count($tokens); $i < $l; $i++) 00472 { 00473 // Remove empty @media, @font-face or @page blocks 00474 if (($tokens[$i][0] == self::T_AT_MEDIA_START && $tokens[$i+1][0] == self::T_AT_MEDIA_END) 00475 || ($tokens[$i][0] == self::T_AT_FONT_FACE_START && $tokens[$i+1][0] == self::T_AT_FONT_FACE_END) 00476 || ($tokens[$i][0] == self::T_AT_PAGE_START && $tokens[$i+1][0] == self::T_AT_PAGE_END)) 00477 { 00478 unset($tokens[$i]); // T_AT_MEDIA_START, T_AT_FONT_FACE_START, T_AT_PAGE_START 00479 unset($tokens[++$i]); // T_AT_MEDIA_END, T_AT_FONT_FACE_END, T_AT_PAGE_END 00480 } 00481 } 00482 $tokens = array_values($tokens); 00483 } 00484 // CSS Level 3 variables: parse variables 00485 if ($sEmulateCcss3Variables) 00486 { 00487 // Parse variables 00488 $variables = array(); 00489 for($i = 0, $l = count($tokens); $i < $l; $i++) 00490 { 00491 if ($tokens[$i][0] == self::T_VARIABLE_DECLARATION) 00492 { 00493 for($i2 = 0, $l2 = count($tokens[$i][3]); $i2 < $l2; $i2++) 00494 { 00495 if (!isset($variables[$tokens[$i][3][$i2]])) 00496 { 00497 $variables[$tokens[$i][3][$i2]] = array(); 00498 } 00499 $variables[$tokens[$i][3][$i2]][$tokens[$i][1]] = $tokens[$i][2]; 00500 } 00501 } 00502 } 00503 } 00504 // Conversion and compression 00505 for($i = 0, $l = count($tokens); $i < $l; $i++) 00506 { 00507 if ($tokens[$i][0] == self::T_DECLARATION) 00508 { 00509 // CSS Level 3 variables 00510 if ($sEmulateCcss3Variables) 00511 { 00512 if (substr($tokens[$i][2], 0, 4) == "var(" && substr($tokens[$i][2], -1, 1) == ")") 00513 { 00514 $tokens[$i][3][] = "all"; 00515 $variable = trim(substr($tokens[$i][2], 4, -1)); 00516 for($i2 = 0, $l2 = count($tokens[$i][3]); $i2 < $l2; $i2++) 00517 { 00518 if (isset($variables[$tokens[$i][3][$i2]][$variable])) 00519 { 00520 $tokens[$i][2] = $variables[$tokens[$i][3][$i2]][$variable]; 00521 break; 00522 } 00523 } 00524 } 00525 } 00526 // Compress unit values 00527 if ($sCompressUnitValues) 00528 { 00529 // Compress "0.5px" to ".5px" 00530 $tokens[$i][2] = preg_replace("/(^| |-)0\.([0-9]+)(%|em|ex|px|in|cm|mm|pt|pc)/iS", "\${1}.\${2}\${3}", $tokens[$i][2]); 00531 // Compress "0px" to "0" 00532 $tokens[$i][2] = preg_replace("/(^| )-?(\.?)0(%|em|ex|px|in|cm|mm|pt|pc)/iS", "\${1}0", $tokens[$i][2]); 00533 // Compress "0 0 0 0" to "0" 00534 if ($tokens[$i][2] == "0 0 0 0") {$tokens[$i][2] = "0";} 00535 } 00536 // Convert RGB color values to hex ("rgb(200,60%,5)" => "#c89905") 00537 if ($sConvertColorValues && preg_match("/rgb\s*\(\s*([0-9%]+)\s*,\s*([0-9%]+)\s*,\s*([0-9%]+)\s*\)/iS", $tokens[$i][2], $m)) 00538 { 00539 for ($i2 = 1, $l2 = count($m); $i2 < $l2; $i2++) 00540 { 00541 if (strpos("%", $m[$i2]) !== false) 00542 { 00543 $m[$i2] = substr($m[$i2], 0, -1); 00544 $m[$i2] = (int) (256 * ($m[$i2] / 100)); 00545 } 00546 $m[$i2] = str_pad(dechex($m[$i2]), 2, "0", STR_PAD_LEFT); 00547 } 00548 $tokens[$i][2] = str_replace($m[0], "#" . $m[1] . $m[2] . $m[3], $tokens[$i][2]); 00549 } 00550 // Compress color values ("#aabbcc" to "#abc") 00551 if ($sCompressColorValues && preg_match("/\#([0-9a-f]{6})/iS", $tokens[$i][2], $m)) 00552 { 00553 $m[1] = strtolower($m[1]); 00554 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)) 00555 { 00556 $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]); 00557 } 00558 } 00559 } 00560 } 00561 // Create minified css 00562 $r = ""; 00563 for($i = 0, $l = count($tokens); $i < $l; $i++) 00564 { 00565 // T_AT_RULE 00566 if ($tokens[$i][0] == self::T_AT_RULE) 00567 { 00568 $r .= "@" . $tokens[$i][1] . " " . $tokens[$i][2] . ";"; 00569 } 00570 // T_AT_MEDIA_START 00571 elseif ($tokens[$i][0] == self::T_AT_MEDIA_START) 00572 { 00573 if (count($tokens[$i][1]) == 1 && $tokens[$i][1][0] == "all") 00574 { 00575 $r .= "@media{"; 00576 } 00577 else 00578 { 00579 $r .= "@media " . implode(",", $tokens[$i][1]) . "{"; 00580 } 00581 } 00582 // T_AT_FONT_FACE_START 00583 elseif ($tokens[$i][0] == self::T_AT_FONT_FACE_START) 00584 { 00585 $r .= "@font-face{"; 00586 } 00587 // T_FONT_FACE_DECLARATION 00588 elseif ($tokens[$i][0] == self::T_FONT_FACE_DECLARATION) 00589 { 00590 $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_AT_FONT_FACE_END ? "" : ";"); 00591 } 00592 // T_AT_PAGE_START 00593 elseif ($tokens[$i][0] == self::T_AT_PAGE_START) 00594 { 00595 $r .= "@page{"; 00596 } 00597 // T_PAGE_DECLARATION 00598 elseif ($tokens[$i][0] == self::T_PAGE_DECLARATION) 00599 { 00600 $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_AT_PAGE_END ? "" : ";"); 00601 } 00602 // T_SELECTORS 00603 elseif ($tokens[$i][0] == self::T_SELECTORS) 00604 { 00605 $r .= implode(",", $tokens[$i][1]); 00606 } 00607 // Start of declarations 00608 elseif ($tokens[$i][0] == self::T_DECLARATIONS_START) 00609 { 00610 $r .= "{"; 00611 } 00612 // T_DECLARATION 00613 elseif ($tokens[$i][0] == self::T_DECLARATION) 00614 { 00615 if ($sConvertCss3Properties && isset(self::$transformations[$tokens[$i][1]])) 00616 { 00617 foreach (self::$transformations[$tokens[$i][1]] as $value) 00618 { 00619 if (!is_array($value)) 00620 { 00621 $r .= $value . ":" . $tokens[$i][2] . ";"; 00622 } 00623 elseif (is_array($value) && is_callable($value)) 00624 { 00625 $r.= call_user_func_array($value, array($tokens[$i][1], $tokens[$i][2])); 00626 00627 } 00628 } 00629 } 00630 $r .= $tokens[$i][1] . ":" . $tokens[$i][2] . ($sRemoveLastSemicolon && $tokens[$i+1][0] == self::T_DECLARATIONS_END ? "" : ";"); 00631 } 00632 // T_DECLARATIONS_END, T_AT_MEDIA_END, T_AT_FONT_FACE_END, T_AT_PAGE_END 00633 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))) 00634 { 00635 $r .= "}"; 00636 } 00637 else 00638 { 00639 // Tokens with no output: 00640 // T_COMMENT 00641 // T_RULESET_START 00642 // T_RULESET_END 00643 // T_AT_VARIABLES_START 00644 // T_VARIABLE_DECLARATION 00645 // T_AT_VARIABLES_END 00646 } 00647 } 00648 return $r; 00649 } 00656 public static function parse($css) 00657 { 00658 // Settings 00659 $sDefaultScope = array("all"); // Default scope 00660 $sDefaultTrim = " \t\n\r\0\x0B"; // Default trim charlist 00661 $sTokenChars = "@{}();:\n\"'/*,"; // Tokens triggering parser processing 00662 // Basic variables 00663 $c = null; // Current char 00664 $p = null; // Previous char 00665 $buffer = ""; // Buffer 00666 $state = array(self::T_DOCUMENT); // State stack 00667 $currentState = self::T_DOCUMENT; // Current state 00668 $scope = $sDefaultScope; // Current scope 00669 $stringChar = null; // String delimiter char 00670 $isFilterWs = true; // Filter double whitespaces? 00671 $selectors = array(); // Array with collected selectors 00672 $r = array(); // Return value 00673 // Prepare css 00674 $css = str_replace("\r\n", "\n", $css); // Windows to Unix line endings 00675 $css = str_replace("\r", "\n", $css); // Mac to Unix line endings 00676 while (strpos($css, "\n\n") !== false) 00677 { 00678 $css = str_replace("\n\n", "\n", $css); // Remove double line endings 00679 } 00680 $css = str_replace("\t", " ", $css); // Convert tabs to spaces 00681 // Parse css 00682 for ($i = 0, $l = strlen($css); $i < $l; $i++) 00683 { 00684 $c = substr($css, $i, 1); 00685 // Filter out double spaces 00686 if ($isFilterWs && $c == " " && $c == $p) 00687 { 00688 continue; 00689 } 00690 $buffer .= $c; 00691 if (strpos($sTokenChars, $c) !== false) 00692 { 00693 // 00694 $currentState = $state[count($state) - 1]; 00695 /* 00696 * Start of comment 00697 */ 00698 if ($p == "/" && $c == "*" && $currentState != self::T_STRING && $currentState != self::T_COMMENT) 00699 { 00700 $saveBuffer = substr($buffer, 0, -2); // save the buffer (will get restored with comment ending) 00701 $buffer = $c; 00702 $isFilterWs = false; 00703 array_push($state, self::T_COMMENT); 00704 } 00705 /* 00706 * End of comment 00707 */ 00708 elseif ($p == "*" && $c == "/" && $currentState == self::T_COMMENT) 00709 { 00710 $r[] = array(self::T_COMMENT, trim($buffer)); 00711 $buffer = $saveBuffer; 00712 $isFilterWs = true; 00713 array_pop($state); 00714 } 00715 /* 00716 * Start of string 00717 */ 00718 elseif (($c == "\"" || $c == "'") && $currentState != self::T_STRING && $currentState != self::T_COMMENT && $currentState != self::T_STRING_URL) 00719 { 00720 $stringChar = $c; 00721 $isFilterWs = false; 00722 array_push($state, self::T_STRING); 00723 } 00727 elseif ($c == "\n" && $p == "\\" && $currentState == self::T_STRING) 00728 { 00729 $buffer = substr($buffer, 0, -2); 00730 } 00731 /* 00732 * End of string 00733 */ 00734 elseif ($c === $stringChar && $currentState == self::T_STRING) 00735 { 00736 if ($p == "\\") // Previous char is a escape char 00737 { 00738 $count = 1; 00739 $i2 = $i -2; 00740 while (substr($css, $i2, 1) == "\\") 00741 { 00742 $count++; 00743 $i2--; 00744 } 00745 // if count of escape chars is uneven => continue with string... 00746 if ($count % 2) 00747 { 00748 continue; 00749 } 00750 } 00751 // ...else end the string 00752 $isFilterWs = true; 00753 array_pop($state); 00754 $stringChar = null; 00755 } 00759 elseif ($c == "(" && ($currentState != self::T_COMMENT && $currentState != self::T_STRING) && strtolower(substr($css, $i - 3, 3) == "url") 00760 && ($currentState == self::T_DECLARATION || $currentState == self::T_FONT_FACE_DECLARATION || $currentState == self::T_PAGE_DECLARATION || $currentState == self::T_VARIABLE_DECLARATION)) 00761 { 00762 array_push($state, self::T_STRING_URL); 00763 } 00767 elseif (($c == ")" || $c == "\n") && ($currentState != self::T_COMMENT && $currentState != self::T_STRING) && $currentState == self::T_STRING_URL) 00768 { 00769 if ($p == "\\") 00770 { 00771 continue; 00772 } 00773 array_pop($state); 00774 } 00775 /* 00776 * Start of at-rule @media block 00777 */ 00778 elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 6)) == "@media") 00779 { 00780 $i = $i + 6; 00781 $buffer = ""; 00782 array_push($state, self::T_AT_MEDIA_START); 00783 } 00784 /* 00785 * At-rule @media block media types 00786 */ 00787 elseif ($c == "{" && $currentState == self::T_AT_MEDIA_START) 00788 { 00789 $buffer = strtolower(trim($buffer, $sDefaultTrim . "{")); 00790 $scope = $buffer != "" ? array_filter(array_map("trim", explode(",", $buffer))) : $sDefaultScope; 00791 $r[] = array(self::T_AT_MEDIA_START, $scope); 00792 $i = $i++; 00793 $buffer = ""; 00794 array_pop($state); 00795 array_push($state, self::T_AT_MEDIA); 00796 } 00797 /* 00798 * End of at-rule @media block 00799 */ 00800 elseif ($currentState == self::T_AT_MEDIA && $c == "}") 00801 { 00802 $r[] = array(self::T_AT_MEDIA_END); 00803 $scope = $sDefaultScope; 00804 $buffer = ""; 00805 array_pop($state); 00806 } 00807 /* 00808 * Start of at-rule @font-face block 00809 */ 00810 elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 10)) == "@font-face") 00811 { 00812 $r[] = array(self::T_AT_FONT_FACE_START); 00813 $i = $i + 10; 00814 $buffer = ""; 00815 array_push($state, self::T_AT_FONT_FACE); 00816 } 00817 /* 00818 * @font-face declaration: Property 00819 */ 00820 elseif ($c == ":" && $currentState == self::T_AT_FONT_FACE) 00821 { 00822 $property = trim($buffer, $sDefaultTrim . ":{"); 00823 $buffer = ""; 00824 array_push($state, self::T_FONT_FACE_DECLARATION); 00825 } 00826 /* 00827 * @font-face declaration: Value 00828 */ 00829 elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_FONT_FACE_DECLARATION) 00830 { 00831 $value = trim($buffer, $sDefaultTrim . ";}"); 00832 $r[] = array(self::T_FONT_FACE_DECLARATION, $property, $value, $scope); 00833 $buffer = ""; 00834 array_pop($state); 00835 if ($c == "}") // @font-face declaration closed with a right curly brace => closes @font-face block 00836 { 00837 array_pop($state); 00838 $r[] = array(self::T_AT_FONT_FACE_END); 00839 } 00840 } 00841 /* 00842 * End of at-rule @font-face block 00843 */ 00844 elseif ($c == "}" && $currentState == self::T_AT_FONT_FACE) 00845 { 00846 $r[] = array(self::T_AT_FONT_FACE_END); 00847 $buffer = ""; 00848 array_pop($state); 00849 } 00850 /* 00851 * Start of at-rule @page block 00852 */ 00853 elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 5)) == "@page") 00854 { 00855 $r[] = array(self::T_AT_PAGE_START); 00856 $i = $i + 5; 00857 $buffer = ""; 00858 array_push($state, self::T_AT_PAGE); 00859 } 00860 /* 00861 * @page declaration: Property 00862 */ 00863 elseif ($c == ":" && $currentState == self::T_AT_PAGE) 00864 { 00865 $property = trim($buffer, $sDefaultTrim . ":{"); 00866 $buffer = ""; 00867 array_push($state, self::T_PAGE_DECLARATION); 00868 } 00869 /* 00870 * @page declaration: Value 00871 */ 00872 elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_PAGE_DECLARATION) 00873 { 00874 $value = trim($buffer, $sDefaultTrim . ";}"); 00875 $r[] = array(self::T_PAGE_DECLARATION, $property, $value, $scope); 00876 $buffer = ""; 00877 array_pop($state); 00878 if ($c == "}") // @page declaration closed with a right curly brace => closes @font-face block 00879 { 00880 array_pop($state); 00881 $r[] = array(self::T_AT_PAGE_END); 00882 } 00883 } 00884 /* 00885 * End of at-rule @page block 00886 */ 00887 elseif ($c == "}" && $currentState == self::T_AT_PAGE) 00888 { 00889 $r[] = array(self::T_AT_PAGE_END); 00890 $buffer = ""; 00891 array_pop($state); 00892 } 00893 /* 00894 * Start of at-rule @variables block 00895 */ 00896 elseif ($c == "@" && $currentState == self::T_DOCUMENT && strtolower(substr($css, $i, 10)) == "@variables") 00897 { 00898 $i = $i + 10; 00899 $buffer = ""; 00900 array_push($state, self::T_AT_VARIABLES_START); 00901 } 00902 /* 00903 * @variables media types 00904 */ 00905 elseif ($c == "{" && $currentState == self::T_AT_VARIABLES_START) 00906 { 00907 $buffer = strtolower(trim($buffer, $sDefaultTrim . "{")); 00908 $r[] = array(self::T_AT_VARIABLES_START, $scope); 00909 $scope = $buffer != "" ? array_filter(array_map("trim", explode(",", $buffer))) : $sDefaultScope; 00910 $i = $i++; 00911 $buffer = ""; 00912 array_pop($state); 00913 array_push($state, self::T_AT_VARIABLES); 00914 } 00915 /* 00916 * @variables declaration: Property 00917 */ 00918 elseif ($c == ":" && $currentState == self::T_AT_VARIABLES) 00919 { 00920 $property = trim($buffer, $sDefaultTrim . ":"); 00921 $buffer = ""; 00922 array_push($state, self::T_VARIABLE_DECLARATION); 00923 } 00924 /* 00925 * @variables declaration: Value 00926 */ 00927 elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_VARIABLE_DECLARATION) 00928 { 00929 $value = trim($buffer, $sDefaultTrim . ";}"); 00930 $r[] = array(self::T_VARIABLE_DECLARATION, $property, $value, $scope); 00931 $buffer = ""; 00932 array_pop($state); 00933 if ($c == "}") // @variable declaration closed with a right curly brace => closes @variables block 00934 { 00935 array_pop($state); 00936 $r[] = array(self::T_AT_VARIABLES_END); 00937 $scope = $sDefaultScope; 00938 } 00939 } 00940 /* 00941 * End of at-rule @variables block 00942 */ 00943 elseif ($c == "}" && $currentState == self::T_AT_VARIABLES) 00944 { 00945 $r[] = array(self::T_AT_VARIABLES_END); 00946 $scope = $sDefaultScope; 00947 $buffer = ""; 00948 array_pop($state); 00949 } 00950 /* 00951 * Start of document level at-rule 00952 */ 00953 elseif ($c == "@" && $currentState == self::T_DOCUMENT) 00954 { 00955 $buffer = ""; 00956 array_push($state, self::T_AT_RULE); 00957 } 00958 /* 00959 * End of document level at-rule 00960 */ 00961 elseif ($c == ";" && $currentState == self::T_AT_RULE) 00962 { 00963 $pos = strpos($buffer, " "); 00964 $rule = substr($buffer, 0, $pos); 00965 $value = trim(substr($buffer, $pos), $sDefaultTrim . ";"); 00966 $r[] = array(self::T_AT_RULE, $rule, $value); 00967 $buffer = ""; 00968 array_pop($state); 00969 } 00973 elseif ($c == "," && ($currentState == self::T_AT_MEDIA || $currentState == self::T_DOCUMENT)) 00974 { 00975 $selectors[]= trim($buffer, $sDefaultTrim . ","); 00976 $buffer = ""; 00977 } 00978 /* 00979 * Start of ruleset 00980 */ 00981 elseif ($c == "{" && ($currentState == self::T_AT_MEDIA || $currentState == self::T_DOCUMENT)) 00982 { 00983 $selectors[]= trim($buffer, $sDefaultTrim . "{"); 00984 $selectors = array_filter(array_map("trim", $selectors)); 00985 $r[] = array(self::T_RULESET_START); 00986 $r[] = array(self::T_SELECTORS, $selectors); 00987 $r[] = array(self::T_DECLARATIONS_START); 00988 $buffer = ""; 00989 $selectors = array(); 00990 array_push($state, self::T_DECLARATIONS); 00991 } 00992 /* 00993 * Declaration: Property 00994 */ 00995 elseif ($c == ":" && $currentState == self::T_DECLARATIONS) 00996 { 00997 $property = trim($buffer, $sDefaultTrim . ":;"); 00998 $buffer = ""; 00999 array_push($state, self::T_DECLARATION); 01000 } 01001 /* 01002 * Declaration: Value 01003 */ 01004 elseif (($c == ";" || $c == "}" || $c == "\n") && $currentState == self::T_DECLARATION) 01005 { 01006 $value = trim($buffer, $sDefaultTrim . ";}"); 01007 $r[] = array(self::T_DECLARATION, $property, $value, $scope); 01008 $buffer = ""; 01009 array_pop($state); 01010 if ($c == "}") // declaration closed with a right curly brace => close ruleset 01011 { 01012 array_pop($state); 01013 $r[] = array(self::T_DECLARATIONS_END); 01014 $r[] = array(self::T_RULESET_END); 01015 } 01016 } 01017 /* 01018 * End of ruleset 01019 */ 01020 elseif ($c == "}" && $currentState == self::T_DECLARATIONS) 01021 { 01022 $r[] = array(self::T_DECLARATIONS_END); 01023 $r[] = array(self::T_RULESET_END); 01024 $buffer = ""; 01025 array_pop($state); 01026 } 01027 01028 } 01029 01030 $p = $c; 01031 } 01032 return $r; 01033 } 01041 private static function _tOpacity($property, $value) 01042 { 01043 $ieValue = (int) ((float) $value * 100); 01044 $r = "-moz-opacity:" . $value . ";"; // Firefox < 3.5 01045 $r .= "-ms-filter: \"alpha(opacity=" . $ieValue . ")\";"; // Internet Explorer 8 01046 $r .= "filter: alpha(opacity=" . $ieValue . ");zoom: 1;"; // Internet Explorer 4 - 7 01047 return $r; 01048 } 01056 private static function _tWhiteSpacePreWrap($property, $value) 01057 { 01058 if (strtolower($value) == "pre-wrap") 01059 { 01060 $r = "white-space:-moz-pre-wrap;"; // Mozilla 01061 $r .= "white-space:-webkit-pre-wrap;"; // Webkit 01062 $r .= "white-space:-khtml-pre-wrap;"; // khtml 01063 $r .= "white-space:-pre-wrap;"; // Opera 4 - 6 01064 $r .= "white-space:-o-pre-wrap;"; // Opera 7+ 01065 $r .= "word-wrap:break-word;"; // Internet Explorer 5.5+ 01066 return $r; 01067 } 01068 else 01069 { 01070 return ""; 01071 } 01072 } 01073 } 01074 ?>