TAG2
define('SELECTOR_LANGUAGE', 11); // SELECTOR:lang(..)
// Used for handling the body 'link' atttribute; this selector have no specificity at all
// we need to introduce this selector type as some ill-brained designers use constructs like:
//
//
//
// test
//
// in this case the CSS rule should have the higher priority; nevertheless, using the default selector rules
// we'd get find that 'link'-generated CSS rule is more important
//
define('SELECTOR_PSEUDOCLASS_LINK_LOW_PRIORITY', 12);
// Used for hanling the following case:
//
//
//
//
//
//
define('SELECTOR_PARENT_LOW_PRIORITY', 13);
define('SELECTOR_PSEUDOELEMENT_BEFORE', 14);
define('SELECTOR_PSEUDOELEMENT_AFTER', 15);
// Note on SELECTOR_ANY:
// normally we should not process rules like
// * html as they're IE specific and (according to CSS standard)
// should be never matched
define('SELECTOR_ANY', 16);
define('SELECTOR_ATTR_VALUE_WORD',17);
// CSS 2.1:
// In CSS2, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [A-Za-z0-9] and
// ISO 10646 characters 161 and higher, plus the hyphen (-); they cannot start with a hyphen or a digit.
// They can also contain escaped characters and any ISO 10646 character as a numeric code (see next item). For instance,
// the identifier "B&W?" may be written as "B\&W\?" or "B\26 W\3F".
//
// Any node can be marked by several space separated class names
//
function node_have_class($root, $target_class) {
if (!$root->has_attribute('class')) { return false; };
$classes = preg_split("/\s+/", strtolower($root->get_attribute('class')));
foreach ($classes as $class) {
if ($class == $target_class) {
return true;
};
};
return false;
};
function match_selector($selector, $root) {
switch ($selector[0]) {
case SELECTOR_TAG:
if ($selector[1] == strtolower($root->tagname())) { return true; };
break;
case SELECTOR_ID:
if ($selector[1] == strtolower($root->get_attribute('id'))) { return true; };
break;
case SELECTOR_CLASS:
if (node_have_class($root, $selector[1])) { return true; }
if ($selector[1] == strtolower($root->get_attribute('class'))) { return true; };
break;
case SELECTOR_TAG_CLASS:
if ((node_have_class($root, $selector[2])) &&
($selector[1] == strtolower($root->tagname()))) { return true; };
break;
case SELECTOR_SEQUENCE:
foreach ($selector[1] as $subselector) {
if (!match_selector($subselector, $root)) { return false; };
};
return true;
case SELECTOR_PARENT:
case SELECTOR_PARENT_LOW_PRIORITY:
$node = $root->parent();
while ($node && $node->node_type() == XML_ELEMENT_NODE) {
if (match_selector($selector[1], $node)) { return true; };
$node = $node->parent();
};
return false;
case SELECTOR_DIRECT_PARENT:
$node = $root->parent();
if ($node && $node->node_type() == XML_ELEMENT_NODE) {
if (match_selector($selector[1], $node)) { return true; };
};
return false;
case SELECTOR_ATTR:
$attr_name = $selector[1];
return $root->has_attribute($attr_name);
case SELECTOR_ATTR_VALUE:
// Note that CSS 2.1 standard does not says strictly if attribute case
// is significiant:
// """
// Attribute values must be identifiers or strings. The case-sensitivity of attribute names and
// values in selectors depends on the document language.
// """
// As we've met several problems with pages having INPUT type attributes in upper (or ewen worse - mixed!)
// case, the following decision have been accepted: attribute values should not be case-sensitive
$attr_name = $selector[1];
$attr_value = $selector[2];
if (!$root->has_attribute($attr_name)) {
return false;
};
return strtolower($root->get_attribute($attr_name)) == strtolower($attr_value);
case SELECTOR_ATTR_VALUE_WORD:
// Note that CSS 2.1 standard does not says strictly if attribute case
// is significiant:
// """
// Attribute values must be identifiers or strings. The case-sensitivity of attribute names and
// values in selectors depends on the document language.
// """
// As we've met several problems with pages having INPUT type attributes in upper (or ewen worse - mixed!)
// case, the following decision have been accepted: attribute values should not be case-sensitive
$attr_name = $selector[1];
$attr_value = $selector[2];
if (!$root->has_attribute($attr_name)) {
return false;
};
$words = preg_split("/\s+/",$root->get_attribute($attr_name));
foreach ($words as $word) {
if (strtolower($word) == strtolower($attr_value)) { return true; };
};
return false;
case SELECTOR_PSEUDOCLASS_LINK:
return $root->tagname() == "a" && $root->has_attribute('href');
case SELECTOR_PSEUDOCLASS_LINK_LOW_PRIORITY:
return $root->tagname() == "a" && $root->has_attribute('href');
// Note that :before and :after always match
case SELECTOR_PSEUDOELEMENT_BEFORE:
return true;
case SELECTOR_PSEUDOELEMENT_AFTER:
return true;
case SELECTOR_LANGUAGE:
// FIXME: determine the document language
return true;
case SELECTOR_ANY:
return true;
};
return false;
}
function css_selector_specificity($selector) {
switch ($selector[0]) {
case SELECTOR_ID:
return array(1,0,0);
case SELECTOR_CLASS:
return array(0,1,0);
case SELECTOR_TAG:
return array(0,0,1);
case SELECTOR_TAG_CLASS:
return array(0,1,1);
case SELECTOR_SEQUENCE:
$specificity = array(0,0,0);
foreach ($selector[1] as $subselector) {
$s = css_selector_specificity($subselector);
$specificity = array($specificity[0]+$s[0],
$specificity[1]+$s[1],
$specificity[2]+$s[2]);
}
return $specificity;
case SELECTOR_PARENT:
return css_selector_specificity($selector[1]);
case SELECTOR_PARENT_LOW_PRIORITY:
return array(-1,-1,-1);
case SELECTOR_DIRECT_PARENT:
return css_selector_specificity($selector[1]);
case SELECTOR_ATTR:
return array(0,1,0);
case SELECTOR_ATTR_VALUE:
return array(0,1,0);
case SELECTOR_ATTR_VALUE_WORD:
return array(0,1,0);
case SELECTOR_PSEUDOCLASS_LINK:
return array(0,1,0);
case SELECTOR_PSEUDOCLASS_LINK_LOW_PRIORITY:
return array(0,0,0);
case SELECTOR_PSEUDOELEMENT_BEFORE:
return array(0,0,0);
case SELECTOR_PSEUDOELEMENT_AFTER:
return array(0,0,0);
case SELECTOR_LANGUAGE:
return array(0,1,0);
case SELECTOR_ANY:
return array(0,1,0);
default:
die("Bad selector while calculating selector specificity:".$selector[0]);
}
}
// Just an abstraction wrapper for determining the selector type
// from the selector-describing structure
//
function selector_get_type($selector) {
return $selector[0];
};
?>