Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
83.33% |
10 / 12 |
CRAP | |
90.38% |
47 / 52 |
TokenList | |
0.00% |
0 / 1 |
|
83.33% |
10 / 12 |
21.39 | |
90.38% |
47 / 52 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
getLength | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
contains | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
add | |
100.00% |
1 / 1 |
4 | |
100.00% |
9 / 9 |
|||
remove | |
100.00% |
1 / 1 |
4 | |
100.00% |
10 / 10 |
|||
current | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
next | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
key | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
valid | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
rewind | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
lazyLoadClassList | |
100.00% |
1 / 1 |
2 | |
100.00% |
6 / 6 |
|||
saveClassList | |
0.00% |
0 / 1 |
3.47 | |
62.50% |
5 / 8 |
<?php | |
declare( strict_types = 1 ); | |
namespace Wikimedia\Parsoid\Utils\DOMCompat; | |
use Iterator; | |
use LogicException; | |
use Wikimedia\Parsoid\DOM\Element; | |
/** | |
* Implements the parts of DOMTokenList interface which are used by Parsoid. | |
* @note To improve performance, no effort is made to keep the TokenList in sync | |
* with the real class list if that is changed from elsewhere. | |
* @see https://dom.spec.whatwg.org/#interface-domtokenlist | |
*/ | |
class TokenList implements Iterator { | |
/** @var Element The node whose classes are listed. */ | |
protected $node; | |
/** @var string Copy of the attribute text, used for change detection. */ | |
private $attribute = false; | |
// Testing element existence with a list is less painful than returning numeric keys | |
// with a map, so let's go with that. | |
/** @var string[] */ | |
private $classList; | |
/** | |
* @param Element $node The node whose classes are listed. | |
*/ | |
public function __construct( $node ) { | |
$this->node = $node; | |
$this->lazyLoadClassList(); | |
} | |
/** | |
* Return the number of CSS classes this element has. | |
* @return int | |
* @see https://dom.spec.whatwg.org/#dom-domtokenlist-length | |
*/ | |
public function getLength(): int { | |
$this->lazyLoadClassList(); | |
return count( $this->classList ); | |
} | |
/** | |
* Checks if the element has a given CSS class. | |
* @param string $token | |
* @return bool | |
* @see https://dom.spec.whatwg.org/#dom-domtokenlist-contains | |
*/ | |
public function contains( string $token ): bool { | |
$this->lazyLoadClassList(); | |
return in_array( $token, $this->classList, true ); | |
} | |
/** | |
* Add CSS classes to the element. | |
* @param string ...$tokens List of classes to add | |
* @see https://dom.spec.whatwg.org/#dom-domtokenlist-add | |
*/ | |
public function add( string ...$tokens ): void { | |
$this->lazyLoadClassList(); | |
$changed = false; | |
foreach ( $tokens as $token ) { | |
if ( !in_array( $token, $this->classList, true ) ) { | |
$changed = true; | |
$this->classList[] = $token; | |
} | |
} | |
if ( $changed ) { | |
$this->saveClassList(); | |
} | |
} | |
/** | |
* Remove CSS classes from the element. | |
* @param string ...$tokens List of classes to remove | |
* @see https://dom.spec.whatwg.org/#dom-domtokenlist-remove | |
*/ | |
public function remove( string ...$tokens ): void { | |
$this->lazyLoadClassList(); | |
$changed = false; | |
foreach ( $tokens as $token ) { | |
$index = array_search( $token, $this->classList, true ); | |
if ( $index !== false ) { | |
array_splice( $this->classList, $index, 1 ); | |
$changed = true; | |
} | |
} | |
if ( $changed ) { | |
$this->saveClassList(); | |
} | |
} | |
/** | |
* @return string | |
*/ | |
public function current() { | |
$this->lazyLoadClassList(); | |
return current( $this->classList ); | |
} | |
/** | |
* @return void | |
*/ | |
public function next() { | |
$this->lazyLoadClassList(); | |
next( $this->classList ); | |
} | |
/** | |
* @return int|null | |
*/ | |
public function key() { | |
$this->lazyLoadClassList(); | |
return key( $this->classList ); | |
} | |
/** | |
* @return bool | |
*/ | |
public function valid() { | |
$this->lazyLoadClassList(); | |
return key( $this->classList ) !== null; | |
} | |
/** | |
* @return void | |
*/ | |
public function rewind() { | |
$this->lazyLoadClassList(); | |
reset( $this->classList ); | |
} | |
/** | |
* Set the classList property based on the class attribute of the wrapped element. | |
*/ | |
private function lazyLoadClassList(): void { | |
$attrib = $this->node->getAttribute( 'class' ) ?? ''; | |
if ( $attrib !== $this->attribute ) { | |
$this->attribute = $attrib; | |
$this->classList = preg_split( '/\s+/', $attrib, -1, | |
PREG_SPLIT_NO_EMPTY ); | |
} | |
} | |
/** | |
* Set the class attribute of the wrapped element based on the classList property. | |
*/ | |
private function saveClassList(): void { | |
if ( $this->classList === null ) { | |
throw new LogicException( 'no class list to set' ); | |
} elseif ( $this->classList === [] ) { | |
$this->attribute = ''; | |
$this->node->removeAttribute( 'class' ); | |
} else { | |
$this->attribute = implode( ' ', $this->classList ); | |
$this->node->setAttribute( 'class', $this->attribute ); | |
} | |
} | |
} |