| Current File : /home/digitaw/www/wp-content/plugins/pojo-accessibility/modules/remediation/actions/styles.php |
<?php
namespace EA11y\Modules\Remediation\Actions;
use DOMDocument;
use DOMElement;
use EA11y\Modules\Remediation\Classes\Remediation_Base;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Class Replace
*/
class Styles extends Remediation_Base {
public static string $type = 'styles';
public static string $style_id = 'ea11y-remediation-styles';
/**
* Build a CSS selector string for a given DOMElement.
*
* @param DOMElement|null $element
* @return string|null
*/
public function get_element_css_selector( ?DOMElement $element ): ?string {
if ( ! $element ) {
return null;
}
$parts = [];
while ( $element && XML_ELEMENT_NODE === $element->nodeType ) {
$selector = strtolower( $element->tagName );
// If element has ID, stop here
if ( $element->hasAttribute( 'id' ) ) {
$selector .= '#' . $element->getAttribute( 'id' );
array_unshift( $parts, $selector );
break;
}
// Add classes unless body
if ( $element->hasAttribute( 'class' ) && strtolower( $element->tagName ) !== 'body' ) {
$classes = preg_split( '/\s+/', trim( $element->getAttribute( 'class' ) ) );
if ( ! empty( $classes ) ) {
$selector .= '.' . implode( '.', $classes );
}
}
// Add nth-of-type if needed
$parent = $element->parentNode;
if ( $parent instanceof DOMElement ) {
$tag_name = $element->tagName;
$siblings = [];
foreach ( $parent->childNodes as $child ) {
if ( $child instanceof DOMElement && $child->tagName === $tag_name ) {
$siblings[] = $child;
}
}
if ( count( $siblings ) > 1 ) {
foreach ( $siblings as $i => $sibling ) {
if ( $sibling->isSameNode( $element ) ) {
$selector .= ':nth-of-type(' . ( $i + 1 ) . ')';
break;
}
}
}
}
array_unshift( $parts, $selector );
$element = $element->parentNode instanceof DOMElement ? $element->parentNode : null;
}
return implode( ' > ', $parts );
}
/**
* Replace CSS selectors for color and background-color rules.
*
* @param string $css The original CSS string
* @param string|null $color_selector New selector for color rule
* @param string|null $bg_selector New selector for background-color rule
* @return string The modified CSS
*/
public function replace_css_selectors(
string $css,
?string $color_selector = null,
?string $bg_selector = null
): string {
// Match full CSS blocks like "selector { ... }"
preg_match_all( '/([^{]+)\{([^}]+)\}/', $css, $matches, PREG_SET_ORDER );
$result = '';
foreach ( $matches as $match ) {
$rules = trim( $match[2] );
// Find color value
if ( $color_selector && preg_match( '/(?<!-)\bcolor\s*:\s*([#a-zA-Z0-9(),.\s%-]+)/i', $rules, $color_match ) ) {
$color_value = trim( $color_match[1] );
$result .= "{$color_selector} { color: {$color_value} !important; }\n";
}
// Find background-color value
if ( $bg_selector && preg_match( '/background-color\s*:\s*([#a-zA-Z0-9(),.\s%-]+)/i', $rules, $bg_match ) ) {
$bg_value = trim( $bg_match[1] );
$result .= "{$bg_selector} { background-color: {$bg_value} !important; }\n";
}
}
return trim( $result );
}
public function run() : ?DOMDocument {
$rule = $this->data['rule'];
if ( $this->data['global'] ) {
$el_color = $this->get_element_by_xpath_with_snippet_fallback( $this->data['xpath'], $this->data['find'] );
$el_bg = $this->get_element_by_xpath_with_snippet_fallback( $this->data['parentXPath'], $this->data['parentFind'] );
$color_css_selector = $this->get_element_css_selector( $el_color );
$bg_css_selector = $this->get_element_css_selector( $el_bg );
if ( ! $color_css_selector && ! $bg_css_selector ) {
$this->use_frontend = true;
return null;
}
$rule = $this->replace_css_selectors( $rule, $color_css_selector, $bg_css_selector );
}
// Find or create <head> element
$head = $this->dom->getElementsByTagName( 'head' )->item( 0 );
if ( ! $head ) {
$head = $this->dom->createElement( 'head' );
$html_element = $this->dom->getElementsByTagName( 'html' )->item( 0 );
if ( $html_element ) {
$html_element->insertBefore( $head, $this->dom->getElementsByTagName( 'body' )->item( 0 ) );
} else {
$this->dom->appendChild( $head );
}
}
// Create <style> tag
$style = $this->dom->getElementById( self::$style_id );
if ( ! $style ) {
$style = $this->dom->createElement( 'style' );
}
$style->setAttribute( 'id', self::$style_id );
$style->appendChild( $this->dom->createTextNode( $rule ) );
// Append to the end of <head>
$head->appendChild( $style );
return $this->dom;
}
}