Browse Source

Added Element::outerText

master
Dustin Wilson 2 years ago
parent
commit
e41ece3a0c
  1. 71
      lib/Element.php
  2. 3
      lib/Inner/Document.php
  3. 60
      tests/cases/TestElement.php

71
lib/Element.php

@ -193,6 +193,77 @@ class Element extends Node {
$parent->replaceChild($fragment, $this);
}
protected function __get_outerText(): ?string {
# The innerText and outerText getter steps are:
# 1. If this is not being rendered or if the user agent is a non-CSS user agent,
# then return this's descendant text content.
// This is a non-CSS user agent. Nothing else to do here.
return $this->__get_textContent();
}
protected function __set_outerText(string $value): void {
# The outerText setter steps are:
# 1. If this's parent is null, then throw a "NoModificationAllowedError"
# DOMException.
$innerNode = $this->innerNode;
if ($this->parentNode === null) {
throw new DOMException(DOMException::NO_MODIFICATION_ALLOWED);
}
# 2. Let next be this's next sibling.
$next = $innerNode->nextSibling;
# 3. Let previous be this's previous sibling.
$previous = $innerNode->previousSibling;
# 4. Let fragment be the rendered text fragment for the given value given this's node
# document.
$fragment = $this->getRenderedTextFragment($value);
# 5. Replace this with fragment within this's parent.
// Check for child nodes before appending to prevent a stupid warning.
if ($fragment->hasChildNodes()) {
$innerNode->parentNode->replaceChild($fragment, $innerNode);
} else {
$innerNode->parentNode->removeChild($innerNode);
}
# 6. If next is non-null and next's previous sibling is a Text node, then merge
# with the next text node given next's previous sibling.
if ($next !== null && $next->previousSibling instanceof \DOMText) {
# To merge with the next text node given a Text node node:
# 1. Let next be node's next sibling.
# 2. If next is not a Text node, then return.
// Already checked for
# 3. Replace data with node, node's data's length, 0, and next's data.
$next->previousSibling->data .= $next->data;
# 4. If next's parent is non-null, then remove next.
// DEVIATION: There are no mutation events in this implementation, so there's no
// reason to check for a parent here.
$next->parentNode->removeChild($next);
}
# 7. If previous is a Text node, then merge with the next text node given previous.
if ($previous instanceof \DOMText) {
# To merge with the next text node given a Text node node:
# 1. Let next be node's next sibling.
$next = $previous->nextSibling;
# 2. If next is not a Text node, then return.
if ($next instanceof \DOMText) {
# 3. Replace data with node, node's data's length, 0, and next's data.
$previous->data .= $next->data;
# 4. If next's parent is non-null, then remove next.
// DEVIATION: There are no mutation events in this implementation, so there's no
// reason to check for a parent here.
$next->parentNode->removeChild($next);
}
}
}
protected function __get_prefix(): ?string {
$prefix = $this->innerNode->prefix;
return ($prefix !== '') ? $prefix : null;

3
lib/Inner/Document.php

@ -53,7 +53,8 @@ class Document extends \DOMDocument {
$this->_wrapperNode = \WeakReference::create($wrapperNode);
if (self::$parentNamespace === null) {
self::$parentNamespace = substr(__NAMESPACE__, 0, strrpos(__NAMESPACE__, '\\'));
// This line is covered, but pcov declares it not covered for some reason...
self::$parentNamespace = substr(__NAMESPACE__, 0, strrpos(__NAMESPACE__, '\\')); // @codeCoverageIgnore
}
}

60
tests/cases/TestElement.php

@ -1375,7 +1375,12 @@ class TestElement extends \PHPUnit\Framework\TestCase {
/**
* @covers \MensBeam\HTML\DOM\Element::__get_innerText
* @covers \MensBeam\HTML\DOM\Element::__set_innerText
* @covers \MensBeam\HTML\DOM\Element::__get_outerText
* @covers \MensBeam\HTML\DOM\Element::__set_outerText
*
* @covers \MensBeam\HTML\DOM\Collection::__construct
* @covers \MensBeam\HTML\DOM\Collection::__get_length
* @covers \MensBeam\HTML\DOM\Collection::count
* @covers \MensBeam\HTML\DOM\Document::__construct
* @covers \MensBeam\HTML\DOM\Document::__get_body
* @covers \MensBeam\HTML\DOM\Document::__get_documentElement
@ -1386,6 +1391,8 @@ class TestElement extends \PHPUnit\Framework\TestCase {
* @covers \MensBeam\HTML\DOM\Element::__get_innerHTML
* @covers \MensBeam\HTML\DOM\Element::getRenderedTextFragment
* @covers \MensBeam\HTML\DOM\Node::__construct
* @covers \MensBeam\HTML\DOM\Node::__get_childNodes
* @covers \MensBeam\HTML\DOM\Node::__get_parentNode
* @covers \MensBeam\HTML\DOM\Node::__get_textContent
* @covers \MensBeam\HTML\DOM\Node::appendChild
* @covers \MensBeam\HTML\DOM\Node::getInnerDocument
@ -1405,21 +1412,58 @@ class TestElement extends \PHPUnit\Framework\TestCase {
* @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor
* @covers \MensBeam\HTML\DOM\Inner\Reflection::getProtectedProperty
*/
public function testProperty_innerText() {
public function testProperty_innerText_outerText() {
$d = new Document();
$d->appendChild($d->createElement('html'));
$d->documentElement->appendChild($d->createElement('body'));
$s = $d->body->appendChild($d->createElement('span'));
$body = $d->body;
$body->appendChild($d->createTextNode('ook '));
$s = $body->appendChild($d->createElement('span'));
$s->appendChild($d->createTextNode('ook'));
$this->assertSame('<span>ook</span>', $d->body->innerHTML);
$d->body->innerText = <<<TEXT
ook
$body->appendChild($d->createTextNode(' eek'));
$this->assertSame('ook <span>ook</span> eek', $body->innerHTML);
$s->innerText = <<<TEXT
ook\r\n
eek ook
TEXT;
$this->assertSame('ookook eek ook', $d->body->innerText);
$this->assertSame('ook<br><br>ook eek ook', $d->body->innerHTML);
$this->assertSame('ook ookook eek ook eek', $body->innerText);
$this->assertSame('ook<br><br>ook eek ook', $s->innerHTML);
$s->outerText = 'ack';
$this->assertSame('ook ack eek', $body->outerText);
$this->assertEquals(1, $body->childNodes->length);
$s = $body->appendChild($d->createElement('span'));
$s->outerText = '';
$this->assertSame('ook ack eek', $body->outerText);
}
/**
* @covers \MensBeam\HTML\DOM\Element::__set_outerText
*
* @covers \MensBeam\HTML\DOM\Document::__construct
* @covers \MensBeam\HTML\DOM\Document::createElement
* @covers \MensBeam\HTML\DOM\DOMException::__construct
* @covers \MensBeam\HTML\DOM\DOMImplementation::__construct
* @covers \MensBeam\HTML\DOM\Element::__construct
* @covers \MensBeam\HTML\DOM\Node::__construct
* @covers \MensBeam\HTML\DOM\Node::__get_parentNode
* @covers \MensBeam\HTML\DOM\Inner\Document::__construct
* @covers \MensBeam\HTML\DOM\Inner\Document::getWrapperNode
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::get
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::has
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::key
* @covers \MensBeam\HTML\DOM\Inner\NodeCache::set
* @covers \MensBeam\HTML\DOM\Inner\Reflection::createFromProtectedConstructor
*/
public function testProperty_outerText__errors() {
$this->expectException(DOMException::class);
$this->expectExceptionCode(DOMException::NO_MODIFICATION_ALLOWED);
$d = new Document();
$h = $d->createElement('html');
$h->outerText = 'fail';
}

Loading…
Cancel
Save