namespace Ice\Filter; /** * Minify css string. * * @package Ice/Filter * @category Minification * @author Ice Team * @copyright (c) 2014-2023 Ice Team * @license http://iceframework.org/license * @uses cssmin.c www.ryanday.org */ class Css { const FREE = 1; const ATRULE = 2; const SELECTOR = 3; const BLOCK = 4; const DECLARATION = 5; const COMMENT = 6; /** * Minify the css. * Removes comments, removes newlines and line feeds keeping, removes last semicolon from last property * * @param string css CSS code to minify * @return string */ public function sanitize(string css) -> string { string min = ""; char c, next; int i, tmp, state = 1, inParen = 0; for i, c in css { let next = css[i + 1]; //closing comment if c == '/' && css[i - 1] == '*' { continue; } if state != self::COMMENT { if c == '/' && next == '*' { let tmp = state, state = self::COMMENT; } } switch state { case self::FREE: if c == ' ' && c == '\n' { let c = 0; } elseif c == '@' { let state = self::ATRULE; break; } elseif c > 0 { let state = self::SELECTOR; } case self::SELECTOR: if c == '{' { let state = self::BLOCK; } elseif c == '\n' { let c = 0; } elseif c == '@' { let state = self::ATRULE; } elseif c == ' ' && next == '{' { let c = 0; } break; case self::ATRULE: /* support @import etc. @font-face{ */ if c == '\n' || c == ';' { let c = ';', state = self::FREE; } elseif c == '{' { let state = self::BLOCK; } break; case self::BLOCK: if c == ' ' || c == '\n' { let c = 0; break; } elseif c == '}' { let state = self::FREE; break; } else { let state = self::DECLARATION; } case self::DECLARATION: //support in paren because data can uris have ; if c == '(' { let inParen = 1; } if inParen == 0 { if c == ';' { let state = self::BLOCK; //could continue peeking through white space.. if next == '}' { let c = 0; } } elseif c == '}' { //handle unterminated declaration let state = self::FREE; } elseif c == '\n' { //skip new lines let c = 0; } elseif c == ' ' { //skip multiple spaces after each other if next == c { let c = 0; } } } elseif c == ')' { let inParen = 0; } break; case self::COMMENT: if c == '*' && next == '/' { let state = tmp; } let c = 0; break; } if c != 0 { let min .= c; } } return min; } }