/** * Parses CSS in $string. The code is saved as array in $this->css * @param string $string the CSS code * @access public * @return bool * @version 1.1 */ function parse($string) { // Temporarily set locale to en_US in order to handle floats properly $old = @setlocale(LC_ALL, 0); @setlocale(LC_ALL, 'C'); // PHP bug? Settings need to be refreshed in PHP4 $this->print = new csstidy_print($this); $this->optimise = new csstidy_optimise($this); $all_properties =& $GLOBALS['csstidy']['all_properties']; $at_rules =& $GLOBALS['csstidy']['at_rules']; $quoted_string_properties =& $GLOBALS['csstidy']['quoted_string_properties']; $this->css = array(); $this->print->input_css = $string; $string = str_replace("\r\n", "\n", $string) . ' '; $cur_comment = ''; for ($i = 0, $size = strlen($string); $i < $size; $i++) { if ($string[$i] === "\n" || $string[$i] === "\r") { ++$this->line; } switch ($this->status) { /* Case in at-block */ case 'at': if (csstidy::is_token($string, $i)) { if ($string[$i] === '/' && @$string[$i + 1] === '*') { $this->status = 'ic'; ++$i; $this->from[] = 'at'; } elseif ($string[$i] === '{') { $this->status = 'is'; $this->at = $this->css_new_media_section($this->at); $this->_add_token(AT_START, $this->at); } elseif ($string[$i] === ',') { $this->at = trim($this->at) . ','; } elseif ($string[$i] === '\\') { $this->at .= $this->_unicode($string, $i); } elseif (in_array($string[$i], array('(', ')', ':', '.'))) { $this->at .= $string[$i]; } } else { $lastpos = strlen($this->at) - 1; if (!((ctype_space($this->at[$lastpos]) || csstidy::is_token($this->at, $lastpos) && $this->at[$lastpos] === ',') && ctype_space($string[$i]))) { $this->at .= $string[$i]; } } break; /* Case in-selector */ /* Case in-selector */ case 'is': if (csstidy::is_token($string, $i)) { if ($string[$i] === '/' && @$string[$i + 1] === '*' && trim($this->selector) == '') { $this->status = 'ic'; ++$i; $this->from[] = 'is'; } elseif ($string[$i] === '@' && trim($this->selector) == '') { // Check for at-rule $this->invalid_at = true; foreach ($at_rules as $name => $type) { if (!strcasecmp(substr($string, $i + 1, strlen($name)), $name)) { $type === 'at' ? $this->at = '@' . $name : ($this->selector = '@' . $name); if ($type === "atis") { $this->next_selector_at = $this->next_selector_at ? $this->next_selector_at : ($this->at ? $this->at : DEFAULT_AT); $this->at = $this->css_new_media_section(' '); $type = "is"; } $this->status = $type; $i += strlen($name); $this->invalid_at = false; } } if ($this->invalid_at) { $this->selector = '@'; $invalid_at_name = ''; for ($j = $i + 1; $j < $size; ++$j) { if (!ctype_alpha($string[$j])) { break; } $invalid_at_name .= $string[$j]; } $this->log('Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning'); } } elseif ($string[$i] === '"' || $string[$i] === "'") { $this->cur_string[] = $string[$i]; $this->status = 'instr'; $this->str_char[] = $string[$i]; $this->from[] = 'is'; /* fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */ $this->quoted_string[] = $string[$i - 1] == '='; } elseif ($this->invalid_at && $string[$i] === ';') { $this->invalid_at = false; $this->status = 'is'; if ($this->next_selector_at) { $this->at = $this->css_new_media_section($this->next_selector_at); $this->next_selector_at = ''; } } elseif ($string[$i] === '{') { $this->status = 'ip'; if ($this->at == '') { $this->at = $this->css_new_media_section(DEFAULT_AT); } $this->selector = $this->css_new_selector($this->at, $this->selector); $this->_add_token(SEL_START, $this->selector); $this->added = false; } elseif ($string[$i] === '}') { $this->_add_token(AT_END, $this->at); $this->at = ''; $this->selector = ''; $this->sel_separate = array(); } elseif ($string[$i] === ',') { $this->selector = trim($this->selector) . ','; $this->sel_separate[] = strlen($this->selector); } elseif ($string[$i] === '\\') { $this->selector .= $this->_unicode($string, $i); } elseif ($string[$i] === '*' && @in_array($string[$i + 1], array('.', '#', '[', ':'))) { // remove unnecessary universal selector, FS#147 } else { $this->selector .= $string[$i]; } } else { $lastpos = strlen($this->selector) - 1; if ($lastpos == -1 || !((ctype_space($this->selector[$lastpos]) || csstidy::is_token($this->selector, $lastpos) && $this->selector[$lastpos] === ',') && ctype_space($string[$i]))) { $this->selector .= $string[$i]; } } break; /* Case in-property */ /* Case in-property */ case 'ip': if (csstidy::is_token($string, $i)) { if (($string[$i] === ':' || $string[$i] === '=') && $this->property != '') { $this->status = 'iv'; if (!$this->get_cfg('discard_invalid_properties') || csstidy::property_is_valid($this->property)) { $this->property = $this->css_new_property($this->at, $this->selector, $this->property); $this->_add_token(PROPERTY, $this->property); } } elseif ($string[$i] === '/' && @$string[$i + 1] === '*' && $this->property == '') { $this->status = 'ic'; ++$i; $this->from[] = 'ip'; } elseif ($string[$i] === '}') { $this->explode_selectors(); $this->status = 'is'; $this->invalid_at = false; $this->_add_token(SEL_END, $this->selector); $this->selector = ''; $this->property = ''; if ($this->next_selector_at) { $this->at = $this->css_new_media_section($this->next_selector_at); $this->next_selector_at = ''; } } elseif ($string[$i] === ';') { $this->property = ''; } elseif ($string[$i] === '\\') { $this->property .= $this->_unicode($string, $i); } elseif ($this->property == '' and !ctype_space($string[$i]) or ($this->property == '/' or $string[$i] == "/")) { $this->property .= $string[$i]; } } elseif (!ctype_space($string[$i])) { $this->property .= $string[$i]; } break; /* Case in-value */ /* Case in-value */ case 'iv': $pn = ($string[$i] === "\n" || $string[$i] === "\r") && $this->property_is_next($string, $i + 1) || $i == strlen($string) - 1; if (csstidy::is_token($string, $i) || $pn) { if ($string[$i] === '/' && @$string[$i + 1] === '*') { $this->status = 'ic'; ++$i; $this->from[] = 'iv'; } elseif ($string[$i] === '"' || $string[$i] === "'" || $string[$i] === '(') { $this->cur_string[] = $string[$i]; $this->str_char[] = $string[$i] === '(' ? ')' : $string[$i]; $this->status = 'instr'; $this->from[] = 'iv'; $this->quoted_string[] = in_array(strtolower($this->property), $quoted_string_properties); } elseif ($string[$i] === ',') { $this->sub_value = trim($this->sub_value) . ','; } elseif ($string[$i] === '\\') { $this->sub_value .= $this->_unicode($string, $i); } elseif ($string[$i] === ';' || $pn) { if ($this->selector[0] === '@' && isset($at_rules[substr($this->selector, 1)]) && $at_rules[substr($this->selector, 1)] === 'iv') { /* Add quotes to charset, import, namespace */ $this->sub_value_arr[] = trim($this->sub_value); $this->status = 'is'; switch ($this->selector) { case '@charset': $this->charset = '"' . $this->sub_value_arr[0] . '"'; break; case '@namespace': $this->namespace = implode(' ', $this->sub_value_arr); break; case '@import': $this->import[] = implode(' ', $this->sub_value_arr); break; } $this->sub_value_arr = array(); $this->sub_value = ''; $this->selector = ''; $this->sel_separate = array(); } else { $this->status = 'ip'; } } elseif ($string[$i] !== '}') { $this->sub_value .= $string[$i]; } if (($string[$i] === '}' || $string[$i] === ';' || $pn) && !empty($this->selector)) { if ($this->at == '') { $this->at = $this->css_new_media_section(DEFAULT_AT); } // case settings if ($this->get_cfg('lowercase_s')) { $this->selector = strtolower($this->selector); } $this->property = strtolower($this->property); $this->optimise->subvalue(); if ($this->sub_value != '') { $this->sub_value_arr[] = $this->sub_value; $this->sub_value = ''; } $this->value = ''; while (count($this->sub_value_arr)) { $sub = array_shift($this->sub_value_arr); if (strstr($this->selector, "font-face")) { $sub = $this->quote_font_format($sub); } if ($sub != '') { $this->value .= ((!strlen($this->value) or substr($this->value, -1, 1) == ',') ? '' : ' ') . $sub; } } $this->optimise->value(); $valid = csstidy::property_is_valid($this->property); if ((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid)) { $this->css_add_property($this->at, $this->selector, $this->property, $this->value); $this->_add_token(VALUE, $this->value); $this->optimise->shorthands(); } if (!$valid) { if ($this->get_cfg('discard_invalid_properties')) { $this->log('Removed invalid property: ' . $this->property, 'Warning'); } else { $this->log('Invalid property in ' . strtoupper($this->get_cfg('css_level')) . ': ' . $this->property, 'Warning'); } } $this->property = ''; $this->sub_value_arr = array(); $this->value = ''; } if ($string[$i] === '}') { $this->explode_selectors(); $this->_add_token(SEL_END, $this->selector); $this->status = 'is'; $this->invalid_at = false; $this->selector = ''; if ($this->next_selector_at) { $this->at = $this->css_new_media_section($this->next_selector_at); $this->next_selector_at = ''; } } } elseif (!$pn) { $this->sub_value .= $string[$i]; if (ctype_space($string[$i])) { $this->optimise->subvalue(); if ($this->sub_value != '') { $this->sub_value_arr[] = $this->sub_value; $this->sub_value = ''; } } } break; /* Case in string */ /* Case in string */ case 'instr': $_str_char = $this->str_char[count($this->str_char) - 1]; $_cur_string = $this->cur_string[count($this->cur_string) - 1]; $_quoted_string = $this->quoted_string[count($this->quoted_string) - 1]; $temp_add = $string[$i]; // Add another string to the stack. Strings can't be nested inside of quotes, only parentheses, but // parentheticals can be nested more than once. if ($_str_char === ")" && ($string[$i] === "(" || $string[$i] === '"' || $string[$i] === '\'') && !csstidy::escaped($string, $i)) { $this->cur_string[] = $string[$i]; $this->str_char[] = $string[$i] == "(" ? ")" : $string[$i]; $this->from[] = 'instr'; $this->quoted_string[] = ($_str_char === ")" and $string[$i] !== "(" and trim($_cur_string) == "(") ? $_quoted_string : !($string[$i] === "("); continue; } if ($_str_char !== ")" && ($string[$i] === "\n" || $string[$i] === "\r") && !($string[$i - 1] === '\\' && !csstidy::escaped($string, $i - 1))) { $temp_add = "\\A"; $this->log('Fixed incorrect newline in string', 'Warning'); } $_cur_string .= $temp_add; if ($string[$i] === $_str_char && !csstidy::escaped($string, $i)) { $this->status = array_pop($this->from); if (!preg_match('|[' . implode('', $GLOBALS['csstidy']['whitespace']) . ']|uis', $_cur_string) && $this->property !== 'content') { if (!$_quoted_string) { if ($_str_char !== ')') { // Convert properties like // font-family: 'Arial'; // to // font-family: Arial; // or // url("abc") // to // url(abc) $_cur_string = substr($_cur_string, 1, -1); } } else { $_quoted_string = false; } } array_pop($this->cur_string); array_pop($this->quoted_string); array_pop($this->str_char); if ($_str_char === ")") { $_cur_string = "(" . trim(substr($_cur_string, 1, -1)) . ")"; } if ($this->status === 'iv') { if (!$_quoted_string) { if (strpos($_cur_string, ',') !== false) { // we can on only remove space next to ',' $_cur_string = implode(',', array_map('trim', explode(',', $_cur_string))); } // and multiple spaces (too expensive) if (strpos($_cur_string, ' ') !== false) { $_cur_string = preg_replace(",\\s+,", " ", $_cur_string); } } $this->sub_value .= $_cur_string; } elseif ($this->status === 'is') { $this->selector .= $_cur_string; } elseif ($this->status === 'instr') { $this->cur_string[count($this->cur_string) - 1] .= $_cur_string; } } else { $this->cur_string[count($this->cur_string) - 1] = $_cur_string; } break; /* Case in-comment */ /* Case in-comment */ case 'ic': if ($string[$i] === '*' && $string[$i + 1] === '/') { $this->status = array_pop($this->from); $i++; $this->_add_token(COMMENT, $cur_comment); $cur_comment = ''; } else { $cur_comment .= $string[$i]; } break; } } $this->optimise->postparse(); $this->print->_reset(); @setlocale(LC_ALL, $old); // Set locale back to original setting return !(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace)); }
/** * Parses CSS in $string. The code is saved as array in $this->css * @param string $string the CSS code * @access public * @return bool * @version 1.1 */ function parse($string) { // PHP bug? Settings need to be refreshed in PHP4 $this->print = new csstidy_print($this); $this->optimise = new csstidy_optimise($this); $all_properties =& $GLOBALS['csstidy']['all_properties']; $at_rules =& $GLOBALS['csstidy']['at_rules']; $this->css = array(); $this->print->input_css = $string; $string = str_replace("\r\n", "\n", $string) . ' '; $cur_comment = ''; for ($i = 0, $size = strlen($string); $i < $size; $i++) { if ($string[$i] == "\n" || $string[$i] == "\r") { ++$this->line; } switch ($this->status) { /* Case in at-block */ case 'at': if (csstidy::is_token($string, $i)) { if ($string[$i] == '/' && @$string[$i + 1] == '*') { $this->status = 'ic'; ++$i; $this->from = 'at'; } elseif ($string[$i] == '{') { $this->status = 'is'; $this->_add_token(AT_START, $this->at); } elseif ($string[$i] == ',') { $this->at = trim($this->at) . ','; } elseif ($string[$i] == '\\') { $this->at .= $this->_unicode($string, $i); } } else { $lastpos = strlen($this->at) - 1; if (!((ctype_space($this->at[$lastpos]) || csstidy::is_token($this->at, $lastpos) && $this->at[$lastpos] == ',') && ctype_space($string[$i]))) { $this->at .= $string[$i]; } } break; /* Case in-selector */ /* Case in-selector */ case 'is': if (csstidy::is_token($string, $i)) { if ($string[$i] == '/' && @$string[$i + 1] == '*' && trim($this->selector) == '') { $this->status = 'ic'; ++$i; $this->from = 'is'; } elseif ($string[$i] == '@' && trim($this->selector) == '') { // Check for at-rule $this->invalid_at = true; foreach ($at_rules as $name => $type) { if (!strcasecmp(substr($string, $i + 1, strlen($name)), $name)) { $type == 'at' ? $this->at = '@' . $name : ($this->selector = '@' . $name); $this->status = $type; $i += strlen($name); $this->invalid_at = false; } } if ($this->invalid_at) { $this->selector = '@'; $invalid_at_name = ''; for ($j = $i + 1; $j < $size; ++$j) { if (!ctype_alpha($string[$j])) { break; } $invalid_at_name .= $string[$j]; } $this->log('Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning'); } } elseif ($string[$i] == '"' || $string[$i] == "'") { $this->cur_string = $string[$i]; $this->status = 'instr'; $this->str_char = $string[$i]; $this->from = 'is'; } elseif ($this->invalid_at && $string[$i] == ';') { $this->invalid_at = false; $this->status = 'is'; } elseif ($string[$i] == '{') { $this->status = 'ip'; $this->_add_token(SEL_START, $this->selector); $this->added = false; } elseif ($string[$i] == '}') { $this->_add_token(AT_END, $this->at); $this->at = ''; $this->selector = ''; $this->sel_separate = array(); } elseif ($string[$i] == ',') { $this->selector = trim($this->selector) . ','; $this->sel_separate[] = strlen($this->selector); } elseif ($string[$i] == '\\') { $this->selector .= $this->_unicode($string, $i); } else { if (!($string[$i] == '*' && @in_array($string[$i + 1], array('.', '#', '[', ':')))) { $this->selector .= $string[$i]; } } } else { $lastpos = strlen($this->selector) - 1; if ($lastpos == -1 || !((ctype_space($this->selector[$lastpos]) || csstidy::is_token($this->selector, $lastpos) && $this->selector[$lastpos] == ',') && ctype_space($string[$i]))) { $this->selector .= $string[$i]; } } break; /* Case in-property */ /* Case in-property */ case 'ip': if (csstidy::is_token($string, $i)) { if (($string[$i] == ':' || $string[$i] == '=') && $this->property != '') { $this->status = 'iv'; if (!$this->get_cfg('discard_invalid_properties') || csstidy::property_is_valid($this->property)) { $this->_add_token(PROPERTY, $this->property); } } elseif ($string[$i] == '/' && @$string[$i + 1] == '*' && $this->property == '') { $this->status = 'ic'; ++$i; $this->from = 'ip'; } elseif ($string[$i] == '}') { $this->explode_selectors(); $this->status = 'is'; $this->invalid_at = false; $this->_add_token(SEL_END, $this->selector); $this->selector = ''; $this->property = ''; } elseif ($string[$i] == ';') { $this->property = ''; } elseif ($string[$i] == '\\') { $this->property .= $this->_unicode($string, $i); } } elseif (!ctype_space($string[$i])) { $this->property .= $string[$i]; } break; /* Case in-value */ /* Case in-value */ case 'iv': $pn = ($string[$i] == "\n" || $string[$i] == "\r") && $this->property_is_next($string, $i + 1) || $i == strlen($string) - 1; if (csstidy::is_token($string, $i) || $pn) { if ($string[$i] == '/' && @$string[$i + 1] == '*') { $this->status = 'ic'; ++$i; $this->from = 'iv'; } elseif ($string[$i] == '"' || $string[$i] == "'" || $string[$i] == '(') { $this->cur_string = $string[$i]; $this->str_char = $string[$i] == '(' ? ')' : $string[$i]; $this->status = 'instr'; $this->from = 'iv'; } elseif ($string[$i] == ',') { $this->sub_value = trim($this->sub_value) . ','; } elseif ($string[$i] == '\\') { $this->sub_value .= $this->_unicode($string, $i); } elseif ($string[$i] == ';' || $pn) { if ($this->selector[0] == '@' && isset($at_rules[substr($this->selector, 1)]) && $at_rules[substr($this->selector, 1)] == 'iv') { $this->sub_value_arr[] = trim($this->sub_value); $this->status = 'is'; switch ($this->selector) { case '@charset': $this->charset = $this->sub_value_arr[0]; break; case '@namespace': $this->namespace = implode(' ', $this->sub_value_arr); break; case '@import': $this->import[] = implode(' ', $this->sub_value_arr); break; } $this->sub_value_arr = array(); $this->sub_value = ''; $this->selector = ''; $this->sel_separate = array(); } else { $this->status = 'ip'; } } elseif ($string[$i] != '}') { $this->sub_value .= $string[$i]; } if (($string[$i] == '}' || $string[$i] == ';' || $pn) && !empty($this->selector)) { if ($this->at == '') { $this->at = DEFAULT_AT; } // case settings if ($this->get_cfg('lowercase_s')) { $this->selector = strtolower($this->selector); } $this->property = strtolower($this->property); $this->optimise->subvalue(); if ($this->sub_value != '') { $this->sub_value_arr[] = $this->sub_value; $this->sub_value = ''; } $this->value = implode(' ', $this->sub_value_arr); $this->selector = trim($this->selector); $this->optimise->value(); $valid = csstidy::property_is_valid($this->property); if ((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid)) { $this->css_add_property($this->at, $this->selector, $this->property, $this->value); $this->_add_token(VALUE, $this->value); $this->optimise->shorthands(); } if (!$valid) { if ($this->get_cfg('discard_invalid_properties')) { $this->log('Removed invalid property: ' . $this->property, 'Warning'); } else { $this->log('Invalid property in ' . strtoupper($this->get_cfg('css_level')) . ': ' . $this->property, 'Warning'); } } $this->property = ''; $this->sub_value_arr = array(); $this->value = ''; } if ($string[$i] == '}') { $this->explode_selectors(); $this->_add_token(SEL_END, $this->selector); $this->status = 'is'; $this->invalid_at = false; $this->selector = ''; } } elseif (!$pn) { $this->sub_value .= $string[$i]; if (ctype_space($string[$i])) { $this->optimise->subvalue(); if ($this->sub_value != '') { $this->sub_value_arr[] = $this->sub_value; $this->sub_value = ''; } } } break; /* Case in string */ /* Case in string */ case 'instr': if ($this->str_char == ')' && ($string[$i] == '"' || $string[$i] == '\'') && !$this->str_in_str && !csstidy::escaped($string, $i)) { $this->str_in_str = true; } elseif ($this->str_char == ')' && ($string[$i] == '"' || $string[$i] == '\'') && $this->str_in_str && !csstidy::escaped($string, $i)) { $this->str_in_str = false; } $temp_add = $string[$i]; // ...and no not-escaped backslash at the previous position if (($string[$i] == "\n" || $string[$i] == "\r") && !($string[$i - 1] == '\\' && !csstidy::escaped($string, $i - 1))) { $temp_add = "\\A "; $this->log('Fixed incorrect newline in string', 'Warning'); } if (!($this->str_char == ')' && in_array($string[$i], $GLOBALS['csstidy']['whitespace']) && !$this->str_in_str)) { $this->cur_string .= $temp_add; } if ($string[$i] == $this->str_char && !csstidy::escaped($string, $i) && !$this->str_in_str) { $this->status = $this->from; if (!preg_match('|[' . implode('', $GLOBALS['csstidy']['whitespace']) . ']|uis', $this->cur_string) && $this->property != 'content') { if ($this->str_char == '"' || $this->str_char == '\'') { $this->cur_string = substr($this->cur_string, 1, -1); } else { if (strlen($this->cur_string) > 3 && ($this->cur_string[1] == '"' || $this->cur_string[1] == '\'')) { $this->cur_string = $this->cur_string[0] . substr($this->cur_string, 2, -2) . substr($this->cur_string, -1); } } } if ($this->from == 'iv') { $this->sub_value .= $this->cur_string; } elseif ($this->from == 'is') { $this->selector .= $this->cur_string; } } break; /* Case in-comment */ /* Case in-comment */ case 'ic': if ($string[$i] == '*' && $string[$i + 1] == '/') { $this->status = $this->from; $i++; $this->_add_token(COMMENT, $cur_comment); $cur_comment = ''; } else { $cur_comment .= $string[$i]; } break; } } $this->optimise->postparse(); $this->print->_reset(); return !(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace)); }
/** * Parses CSS in $string. The code is saved as array in $this->css * @param string $string the CSS code * @access public * @return bool * @version 1.1 */ function parse($string) { // Temporarily set locale to en_US in order to handle floats properly $old = @setlocale(LC_ALL, 0); @setlocale(LC_ALL, 'C'); // PHP bug? Settings need to be refreshed in PHP4 $this->print = new csstidy_print($this); $this->optimise = new csstidy_optimise($this); $all_properties =& $GLOBALS['csstidy']['all_properties']; $at_rules =& $GLOBALS['csstidy']['at_rules']; $this->css = array(); $this->print->input_css = $string; $string = str_replace("\r\n", "\n", $string) . ' '; $cur_comment = ''; for ($i = 0, $size = strlen($string); $i < $size; $i++) { if ($string[$i] === "\n" || $string[$i] === "\r") { ++$this->line; } switch ($this->status) { /* Case in at-block */ case 'at': if (csstidy::is_token($string, $i)) { if ($string[$i] === '/' && @$string[$i + 1] === '*') { $this->status = 'ic'; ++$i; $this->from = 'at'; } elseif ($string[$i] === '{') { $this->status = 'is'; $this->at = $this->css_new_media_section($this->at); $this->_add_token(AT_START, $this->at); } elseif ($string[$i] === ',') { $this->at = trim($this->at) . ','; } elseif ($string[$i] === '\\') { $this->at .= $this->_unicode($string, $i); } elseif (in_array($string[$i], array('(', ')', ':'))) { $this->at .= $string[$i]; } } else { $lastpos = strlen($this->at) - 1; if (!((ctype_space($this->at[$lastpos]) || csstidy::is_token($this->at, $lastpos) && $this->at[$lastpos] === ',') && ctype_space($string[$i]))) { $this->at .= $string[$i]; } } break; /* Case in-selector */ /* Case in-selector */ case 'is': if (csstidy::is_token($string, $i)) { if ($string[$i] === '/' && @$string[$i + 1] === '*' && trim($this->selector) == '') { $this->status = 'ic'; ++$i; $this->from = 'is'; } elseif ($string[$i] === '@' && trim($this->selector) == '') { // Check for at-rule $this->invalid_at = true; foreach ($at_rules as $name => $type) { if (!strcasecmp(substr($string, $i + 1, strlen($name)), $name)) { $type === 'at' ? $this->at = '@' . $name : ($this->selector = '@' . $name); $this->status = $type; $i += strlen($name); $this->invalid_at = false; } } if ($this->invalid_at) { $this->selector = '@'; $invalid_at_name = ''; for ($j = $i + 1; $j < $size; ++$j) { if (!ctype_alpha($string[$j])) { break; } $invalid_at_name .= $string[$j]; } $this->log('Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning'); } } elseif ($string[$i] === '"' || $string[$i] === "'") { $this->cur_string = $string[$i]; $this->status = 'instr'; $this->str_char = $string[$i]; $this->from = 'is'; /* fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */ $this->quoted_string = $string[$i - 1] == '='; } elseif ($this->invalid_at && $string[$i] === ';') { $this->invalid_at = false; $this->status = 'is'; } elseif ($string[$i] === '{') { $this->status = 'ip'; if ($this->at == '') { $this->at = $this->css_new_media_section(DEFAULT_AT); } $this->selector = $this->css_new_selector($this->at, $this->selector); $this->_add_token(SEL_START, $this->selector); $this->added = false; } elseif ($string[$i] === '}') { $this->_add_token(AT_END, $this->at); $this->at = ''; $this->selector = ''; $this->sel_separate = array(); } elseif ($string[$i] === ',') { $this->selector = trim($this->selector) . ','; $this->sel_separate[] = strlen($this->selector); } elseif ($string[$i] === '\\') { $this->selector .= $this->_unicode($string, $i); } elseif ($string[$i] === '*' && @in_array($string[$i + 1], array('.', '#', '[', ':'))) { // remove unnecessary universal selector, FS#147 } else { $this->selector .= $string[$i]; } } else { $lastpos = strlen($this->selector) - 1; if ($lastpos == -1 || !((ctype_space($this->selector[$lastpos]) || csstidy::is_token($this->selector, $lastpos) && $this->selector[$lastpos] === ',') && ctype_space($string[$i]))) { $this->selector .= $string[$i]; } } break; /* Case in-property */ /* Case in-property */ case 'ip': if (csstidy::is_token($string, $i)) { if (($string[$i] === ':' || $string[$i] === '=') && $this->property != '') { $this->status = 'iv'; if (!$this->get_cfg('discard_invalid_properties') || csstidy::property_is_valid($this->property)) { $this->property = $this->css_new_property($this->at, $this->selector, $this->property); $this->_add_token(PROPERTY, $this->property); } } elseif ($string[$i] === '/' && @$string[$i + 1] === '*' && $this->property == '') { $this->status = 'ic'; ++$i; $this->from = 'ip'; } elseif ($string[$i] === '}') { $this->explode_selectors(); $this->status = 'is'; $this->invalid_at = false; $this->_add_token(SEL_END, $this->selector); $this->selector = ''; $this->property = ''; } elseif ($string[$i] === ';') { $this->property = ''; } elseif ($string[$i] === '\\') { $this->property .= $this->_unicode($string, $i); } elseif ($this->property == '' and !ctype_space($string[$i])) { $this->property .= $string[$i]; } } elseif (!ctype_space($string[$i])) { $this->property .= $string[$i]; } break; /* Case in-value */ /* Case in-value */ case 'iv': $pn = ($string[$i] === "\n" || $string[$i] === "\r") && $this->property_is_next($string, $i + 1) || $i == strlen($string) - 1; if (csstidy::is_token($string, $i) || $pn) { if ($string[$i] === '/' && @$string[$i + 1] === '*') { $this->status = 'ic'; ++$i; $this->from = 'iv'; } elseif ($string[$i] === '"' || $string[$i] === "'" || $string[$i] === '(') { $this->cur_string = $string[$i]; $this->str_char = $string[$i] === '(' ? ')' : $string[$i]; $this->status = 'instr'; $this->from = 'iv'; } elseif ($string[$i] === ',') { $this->sub_value = trim($this->sub_value) . ','; } elseif ($string[$i] === '\\') { $this->sub_value .= $this->_unicode($string, $i); } elseif ($string[$i] === ';' || $pn) { if ($this->selector[0] === '@' && isset($at_rules[substr($this->selector, 1)]) && $at_rules[substr($this->selector, 1)] === 'iv') { /* Add quotes to charset, import, namespace */ $this->sub_value_arr[] = '"' . trim($this->sub_value) . '"'; $this->status = 'is'; switch ($this->selector) { case '@charset': $this->charset = $this->sub_value_arr[0]; break; case '@namespace': $this->namespace = implode(' ', $this->sub_value_arr); break; case '@import': $this->import[] = implode(' ', $this->sub_value_arr); break; } $this->sub_value_arr = array(); $this->sub_value = ''; $this->selector = ''; $this->sel_separate = array(); } else { $this->status = 'ip'; } } elseif ($string[$i] !== '}') { $this->sub_value .= $string[$i]; } if (($string[$i] === '}' || $string[$i] === ';' || $pn) && !empty($this->selector)) { if ($this->at == '') { $this->at = $this->css_new_media_section(DEFAULT_AT); } // case settings if ($this->get_cfg('lowercase_s')) { $this->selector = strtolower($this->selector); } $this->property = strtolower($this->property); $this->optimise->subvalue(); if ($this->sub_value != '') { if (substr($this->sub_value, 0, 6) == 'format') { $this->sub_value = str_replace(array('format(', ')'), array('format("', '")'), $this->sub_value); } $this->sub_value_arr[] = $this->sub_value; $this->sub_value = ''; } $this->value = array_shift($this->sub_value_arr); while (count($this->sub_value_arr)) { $this->value .= (substr($this->value, -1, 1) == ',' ? '' : ' ') . array_shift($this->sub_value_arr); } $this->optimise->value(); $valid = csstidy::property_is_valid($this->property); if ((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid)) { $this->css_add_property($this->at, $this->selector, $this->property, $this->value); $this->_add_token(VALUE, $this->value); $this->optimise->shorthands(); } if (!$valid) { if ($this->get_cfg('discard_invalid_properties')) { $this->log('Removed invalid property: ' . $this->property, 'Warning'); } else { $this->log('Invalid property in ' . strtoupper($this->get_cfg('css_level')) . ': ' . $this->property, 'Warning'); } } $this->property = ''; $this->sub_value_arr = array(); $this->value = ''; } if ($string[$i] === '}') { $this->explode_selectors(); $this->_add_token(SEL_END, $this->selector); $this->status = 'is'; $this->invalid_at = false; $this->selector = ''; } } elseif (!$pn) { $this->sub_value .= $string[$i]; if (ctype_space($string[$i])) { $this->optimise->subvalue(); if ($this->sub_value != '') { $this->sub_value_arr[] = $this->sub_value; $this->sub_value = ''; } } } break; /* Case in string */ /* Case in string */ case 'instr': if ($this->str_char === ')' && ($string[$i] === '"' || $string[$i] === '\'') && !$this->str_in_str && !csstidy::escaped($string, $i)) { $this->str_in_str = true; } elseif ($this->str_char === ')' && ($string[$i] === '"' || $string[$i] === '\'') && $this->str_in_str && !csstidy::escaped($string, $i)) { $this->str_in_str = false; } $temp_add = $string[$i]; // ...and no not-escaped backslash at the previous position if (($string[$i] === "\n" || $string[$i] === "\r") && !($string[$i - 1] === '\\' && !csstidy::escaped($string, $i - 1))) { $temp_add = "\\A "; $this->log('Fixed incorrect newline in string', 'Warning'); } // this optimisation remove space in css3 properties (see vendor-prefixed/webkit-gradient.csst) #if (!($this->str_char === ')' && in_array($string{$i}, $GLOBALS['csstidy']['whitespace']) && !$this->str_in_str)) { $this->cur_string .= $temp_add; #} if ($string[$i] == $this->str_char && !csstidy::escaped($string, $i) && !$this->str_in_str) { $this->status = $this->from; if (!preg_match('|[' . implode('', $GLOBALS['csstidy']['whitespace']) . ']|uis', $this->cur_string) && $this->property !== 'content') { if (!$this->quoted_string) { if ($this->str_char === '"' || $this->str_char === '\'') { // Temporarily disable this optimization to avoid problems with @charset rule, quote properties, and some attribute selectors... // Attribute selectors fixed, added quotes to @chartset, no problems with properties detected. Enabled $this->cur_string = substr($this->cur_string, 1, -1); } else { if (strlen($this->cur_string) > 3 && ($this->cur_string[1] === '"' || $this->cur_string[1] === '\'')) { $this->cur_string = $this->cur_string[0] . substr($this->cur_string, 2, -2) . substr($this->cur_string, -1); } } } else { $this->quoted_string = false; } } if ($this->from === 'iv') { if (!$this->quoted_string) { if (strpos($this->cur_string, ',') !== false) { // we can on only remove space next to ',' $this->cur_string = implode(',', array_map('trim', explode(',', $this->cur_string))); } // and multiple spaces (too expensive) if (strpos($this->cur_string, ' ') !== false) { $this->cur_string = preg_replace(",\\s+,", " ", $this->cur_string); } } $this->sub_value .= $this->cur_string; } elseif ($this->from === 'is') { $this->selector .= $this->cur_string; } } break; /* Case in-comment */ /* Case in-comment */ case 'ic': if ($string[$i] === '*' && $string[$i + 1] === '/') { $this->status = $this->from; $i++; $this->_add_token(COMMENT, $cur_comment); $cur_comment = ''; } else { $cur_comment .= $string[$i]; } break; } } $this->optimise->postparse(); $this->print->_reset(); @setlocale(LC_ALL, $old); // Set locale back to original setting return !(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace)); }