get_attribute("style"); // Some "designers" (obviously lacking the brain and ability to read ) use such constructs: // // // // It is out of standard, as HTML 4.01 says: // // The syntax of the value of the style attribute is determined by the default style sheet language. // For example, for [[CSS2]] inline style, use the declaration block syntax described in section 4.1.8 // *(without curly brace delimiters)* // // but still parsed by many browsers; let's be compatible with these idiots - remove curly braces // $style = preg_replace("/^\s*{/","",$style); $style = preg_replace("/}\s*$/","",$style); $properties = parse_css_properties($style, $pipeline); $rule = new CSSRule(array( array(SELECTOR_ANY), $properties, $pipeline->get_base_url(), $root ), $pipeline); $rule->apply($root, $state, $pipeline); } // TODO: make a real parser instead of if-then-else mess // // Selector grammar (according to CSS 2.1, paragraph 5.1 & 5.2): // Note that this particular grammar is not LL1, but still can be converter to // that form // // COMPOSITE_SELECTOR ::= SELECTOR ("," SELECTOR)* // // SELECTOR ::= SIMPLE_SELECTOR (COMBINATOR SIMPLE_SELECTOR)* // // COMBINATOR ::= WHITESPACE* COMBINATOR_SYMBOL WHITESPACE* // COMBINATOR_SYMBOL ::= " " | ">" | "+" // // SIMPLE_SELECTOR ::= TYPE_SELECTOR (ADDITIONAL_SELECTOR)* // SIMPLE_SELECTOR ::= UNIVERSAL_SELECTOR (ADDITIONAL_SELECTOR)* // SIMPLE_SELECTOR ::= (ADDITIONAL_SELECTOR)* // // CSS 2.1, p. 5.3: if the universal selector is not the only component of a simple selector, the "*" may be omitted // SIMPLE_SELECTOR ::= (ADDITIONAL_SELECTOR)* // // TYPE_SELECTOR ::= TAG_NAME // // UNIVERSAL_SELECTOR ::= "*" // // ADDITIONAL_SELECTOR ::= ATTRIBUTE_SELECTOR | ID_SELECTOR | PSEUDOCLASS | CLASS_SELECTOR | PSEUDOELEMENT // // ATTRIBUTE_SELECTOR ::= "[" ATTRIBUTE_NAME "]" // ATTRIBUTE_SELECTOR ::= "[" ATTRIBUTE_NAME "=" ATTR_VALUE "]" // ATTRIBUTE_SELECTOR ::= "[" ATTRIBUTE_NAME "~=" ATTR_VALUE "]" // ATTRIBUTE_SELECTOR ::= "[" ATTRIBUTE_NAME "|=" ATTR_VALUE "]" // // CLASS_SELECTOR ::= "." CLASS_NAME // // ID_SELECTOR ::= "#" ID_VALUE // // PSEUDOCLASS ::= ":first-child" | // ":link" | // ":visited" | // ignored in our case // ":hover" | // dynamic - ignored in our case // ":active" | // dynamic - ignored in our case // ":focus" | // dynamic - ignored in our case // ":lang(" LANG ")" | // dynamic - ignored in our case // // PSEUDOELEMENT ::= ":first-line" | // ":first-letter" | // ":before" | // ":after" | // // ATTR_VALUE ::= IDENTIFIER | STRING // CLASS_NAME ::= INDETIFIER // ID_VALUE ::= IDENTIFIER // function parse_css_selector($raw_selector) { // Note a 'trim' call. Is is required as there could be leading/trailing spaces in $raw_selector // $raw_selector = strtolower(trim($raw_selector)); // Direct Parent/child selectors (for example 'table > tr') if (preg_match("/^(\S.*)\s*>\s*([^\s]+)$/", $raw_selector, $matches)) { return array(SELECTOR_SEQUENCE, array( parse_css_selector($matches[2]), array(SELECTOR_DIRECT_PARENT, parse_css_selector($matches[1])))); } // Parent/child selectors (for example 'table td') if (preg_match("/^(\S.*)\s+([^\s]+)$/", $raw_selector, $matches)) { return array(SELECTOR_SEQUENCE, array( parse_css_selector($matches[2]), array(SELECTOR_PARENT, parse_css_selector($matches[1])))); } if (preg_match("/^(.+)\[(".SELECTOR_ATTR_REGEXP.")\]$/", $raw_selector, $matches)) { return array(SELECTOR_SEQUENCE, array( parse_css_selector($matches[1]), array(SELECTOR_ATTR, $matches[2]))); } if (preg_match("/^(.+)\[".SELECTOR_ATTR_VALUE_REGEXP."\]$/", $raw_selector, $matches)) { return array(SELECTOR_SEQUENCE, array( parse_css_selector($matches[1]), array(SELECTOR_ATTR_VALUE, $matches[2], css_remove_value_quotes($matches[3])))); } if (preg_match("/^(.+)\[".SELECTOR_ATTR_VALUE_WORD_REGEXP."\]$/", $raw_selector, $matches)) { return array(SELECTOR_SEQUENCE, array( parse_css_selector($matches[1]), array(SELECTOR_ATTR_VALUE_WORD, $matches[2], css_remove_value_quotes($matches[3])))); } // pseudoclasses & pseudoelements if (preg_match("/^([#\.\s\w_-]*):(\w+)$/", $raw_selector, $matches)) { if ($matches[1] === "") { $matches[1] = "*"; }; switch($matches[2]) { case "lowlink": return array(SELECTOR_SEQUENCE, array(parse_css_selector($matches[1]), array(SELECTOR_PSEUDOCLASS_LINK_LOW_PRIORITY))); case "link": return array(SELECTOR_SEQUENCE, array(parse_css_selector($matches[1]), array(SELECTOR_PSEUDOCLASS_LINK))); case "before": return array(SELECTOR_SEQUENCE, array(parse_css_selector($matches[1]), array(SELECTOR_PSEUDOELEMENT_BEFORE))); case "after": return array(SELECTOR_SEQUENCE, array(parse_css_selector($matches[1]), array(SELECTOR_PSEUDOELEMENT_AFTER))); }; }; // :lang() pseudoclass if (preg_match("/^([#\.\s\w_-]+):lang\((\w+)\)$/", $raw_selector, $matches)) { return array(SELECTOR_SEQUENCE, array(parse_css_selector($matches[1]), array(SELECTOR_LANGUAGE, $matches[2]))); }; if (preg_match("/^(\S+)(\.\S+)$/", $raw_selector, $matches)) { return array(SELECTOR_SEQUENCE, array(parse_css_selector($matches[1]), parse_css_selector($matches[2]))); }; switch ($raw_selector{0}) { case '#': return array(SELECTOR_ID, substr($raw_selector,1)); case '.': return array(SELECTOR_CLASS, substr($raw_selector,1)); }; if (preg_match("/^(\w+)#(".SELECTOR_ID_REGEXP.")$/", $raw_selector, $matches)) { return array(SELECTOR_SEQUENCE, array(array(SELECTOR_ID, $matches[2]), array(SELECTOR_TAG, $matches[1]))); }; if ($raw_selector === "*") { return array(SELECTOR_ANY); }; return array(SELECTOR_TAG,$raw_selector); } function parse_css_selectors($raw_selectors) { $offset = 0; $selectors = array(); $selector_strings = explode(",",$raw_selectors); foreach ($selector_strings as $selector_string) { // See comment on SELECTOR_ANY regarding why this code is commented // Remove the '* html' string from the selector // $selector_string = preg_replace('/^\s*\*\s+html/','',$selector_string); $selector_string = trim($selector_string); // Support for non-valid CSS similar to: "selector1,selector2, {rules}" // In this case we'll get three selectors; last will be empty string if (!empty($selector_string)) { $selectors[] = parse_css_selector($selector_string); }; }; return $selectors; } // function &parse_css_property($property, &$pipeline) { // if (preg_match("/^(.*?)\s*:\s*(.*)/",$property, $matches)) { // $name = strtolower(trim($matches[1])); // $code = CSS::word2code($name); // if (is_null($code)) { // error_log(sprintf("Unsupported CSS property: '%s'", $name)); // $null = null; // return $null; // }; // $collection =& new CSSPropertyCollection(); // $collection->addProperty(CSSPropertyDeclaration::create($code, trim($matches[2]), $pipeline)); // return $collection; // } elseif (preg_match("/@import\s+\"(.*)\";/",$property, $matches)) { // // @import "" // $collection =& css_import(trim($matches[1]), $pipeline); // return $collection; // } elseif (preg_match("/@import\s+url\((.*)\);/",$property, $matches)) { // // @import url() // $collection =& css_import(trim($matches[1]), $pipeline); // return $collection; // } elseif (preg_match("/@import\s+(.*);/",$property, $matches)) { // // @import // $collection =& css_import(trim($matches[1]), $pipeline); // return $collection; // } else { // $collection =& new CSSPropertyCollection(); // return $collection; // }; // } ?>