Browse Source

Trying out HTMLElement

element-classes
Dustin Wilson 3 years ago
parent
commit
5f1b388cf7
  1. 10
      lib/Document.php
  2. 14
      lib/Element.php
  3. 86
      lib/HTMLElement.php
  4. 6
      lib/TokenList.php
  5. 26
      lib/traits/ParentNode.php

10
lib/Document.php

@ -30,7 +30,7 @@ class Document extends AbstractDocument {
protected const VOID_ELEMENTS = [ 'area', 'base', 'basefont', 'bgsound', 'br', 'col', 'embed', 'frame', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr' ];
public function __get_body(): \DOMNode {
protected function __get_body(): \DOMNode {
if ($this->documentElement === null || $this->documentElement->childNodes->length === 0) {
return null;
}
@ -63,7 +63,7 @@ class Document extends AbstractDocument {
return null;
}
public function __set_body($value) {
protected function __set_body($value) {
# On setting, the following algorithm must be run:
#
# 1. If the new value is not a body or frameset element, then throw a
@ -100,15 +100,15 @@ class Document extends AbstractDocument {
$this->_body = $value;
}
public function __get_documentEncoding(): ?string {
protected function __get_documentEncoding(): ?string {
return $this->_documentEncoding;
}
public function __get_quirksMode(): int {
protected function __get_quirksMode(): int {
return $this->_quirksMode;
}
public function __get_xpath(): \DOMXPath {
protected function __get_xpath(): \DOMXPath {
if ($this->_xpath === null) {
$this->_xpath = new \DOMXPath($this);
}

14
lib/Element.php

@ -12,7 +12,7 @@ class Element extends \DOMElement {
protected $_classList;
public function __get_classList(): ?TokenList {
protected function __get_classList(): ?TokenList {
// MensBeam\HTML\DOM\TokenList uses WeakReference to prevent a circular reference,
// so it requires PHP 7.4 to work.
if (version_compare(\PHP_VERSION, '7.4.0', '>=')) {
@ -25,7 +25,7 @@ class Element extends \DOMElement {
return null; // @codeCoverageIgnore
}
public function __get_innerHTML(): string {
protected function __get_innerHTML(): string {
### DOM Parsing Specification ###
# 2.3 The InnerHTML mixin
#
@ -37,7 +37,7 @@ class Element extends \DOMElement {
return $this->ownerDocument->serialize($this);
}
public function __set_innerHTML(string $value) {
protected function __set_innerHTML(string $value) {
### DOM Parsing Specification ###
# 2.3 The InnerHTML mixin
#
@ -96,7 +96,7 @@ class Element extends \DOMElement {
}
}
public function __get_nextElementSibling(): Element {
protected function __get_nextElementSibling(): Element {
# The nextElementSibling getter steps are to return the first following sibling
# that is an element; otherwise null.
if ($this->parentNode !== null) {
@ -121,7 +121,7 @@ class Element extends \DOMElement {
return null;
}
public function __get_outerHTML(): string {
protected function __get_outerHTML(): string {
### DOM Parsing Specification ###
# 2.4 Extensions to the Element interface
# outerHTML
@ -135,7 +135,7 @@ class Element extends \DOMElement {
return $this->__toString();
}
public function __set_outerHTML(string $value) {
protected function __set_outerHTML(string $value) {
### DOM Parsing Specification ###
# 2.4 Extensions to the Element interface
# outerHTML
@ -172,7 +172,7 @@ class Element extends \DOMElement {
$this->parentNode->replaceChild($fragment, $this);
}
public function __get_previousElementSibling(): Element {
protected function __get_previousElementSibling(): Element {
# The previousElementSibling getter steps are to return the first preceding
# sibling that is an element; otherwise null.
if ($this->parentNode !== null) {

86
lib/HTMLElement.php

@ -0,0 +1,86 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
class HTMLElement extends Element {
protected function __get_accessKey(): string {
# The accessKey IDL attribute must reflect the accesskey content attribute.
return $this->getAttribute('accesskey');
}
protected function __set_accessKey(string $value) {
return $this->setAttribute('accesskey', $value);
}
protected function __get_contentEditable(): string {
# The contentEditable IDL attribute, on getting, must return the string "true"
# if the content attribute is set to the true state, "false" if the content
# attribute is set to the false state, and "inherit" otherwise.
$result = $this->getAttribute('contenteditable');
switch ($value) {
case 'false':
case 'true':
return $result;
default:
return 'inherit';
}
}
protected function __set_contentEditable(string $value) {
# On setting, if the new value is an ASCII case-insensitive match for the
# string "inherit" then the content attribute must be removed, if the new value
# is an ASCII case-insensitive match for the string "true" then the content
# attribute must be set to the string "true", if the new value is an ASCII
# case-insensitive match for the string "false" then the content attribute must
# be set to the string "false", and otherwise the attribute setter must throw a
# "SyntaxError" DOMException.
switch ($value) {
case 'inherit'
$this->removeAttribute('contenteditable');
case 'false':
case 'true':
return $this->setAttribute('contenteditable', $value);
default:
throw new DOMException(DOMException::SYNTAX_ERROR);
}
}
protected function __get_isContentEditable(): bool {
# The isContentEditable IDL attribute, on getting, must return true if the
# element is either an editing host or editable, and false otherwise.
#
# An editing host is either an HTML element with its contenteditable attribute
# in the true state, or a child HTML element of a Document whose design mode
# enabled is true.
#
# Something is editable if it is a node; it is not an editing host; it does
# not have a contenteditable attribute set to the false state; its parent is an
# editing host or editable; and either it is an HTML element, or it is an svg or
# math element, or it is not an Element and its parent is an HTML element.
$contentEditable = $this->__get_contentEditable();
$designMode = ($this->ownerDocument->designMode === 'on');
if ($contentEditable === 'true' || $designMode) {
return true;
} elseif ($contentEditable !== 'false') {
// If the parent can be either an editing host or editable then all is needed
// is to see if there's an ancestor that's an editing host. Just seems absurd
// to word the specification like that. Since isContentEditable is a property
// of HTMLElement there's no need to check if it's an HTML element, svg, or
// non-element child of foreign content. There is also no need to check for
// design mode enabled on the document because it's checked above.
if ($this->moonwalk(function($n) {
if ($n instanceof HTMLElement && $n->contentEditable === 'true') {
return true;
}
})->current() !== null) {
return true;
}
}
return false;
}
}

6
lib/TokenList.php

@ -21,15 +21,15 @@ class TokenList implements \ArrayAccess, \Countable, \Iterator {
private const ASCII_WHITESPACE_REGEX = '/[\t\n\x0c\r ]+/';
public function __get_length(): int {
protected function __get_length(): int {
return $this->_length;
}
public function __get_value(): string {
protected function __get_value(): string {
return $this->__toString();
}
public function __set_value(string $value) {
protected function __get_value(string $value) {
$this->tokenSet = $this->parseOrderedSet($value);
$this->_length = count($this->tokenSet);
}

26
lib/traits/ParentNode.php

@ -9,7 +9,7 @@ namespace MensBeam\HTML\DOM;
if (version_compare(\PHP_VERSION, '8.0', '>=')) {
# 4.2.6. Mixin ParentNode
trait ParentNode {
public function __get_children(): \DOMNodeList {
protected function __get_children(): \DOMNodeList {
# The children getter steps are to return an HTMLCollection collection rooted at
# this matching only element children.
// DEVIATION: HTMLCollection doesn't exist in PHP's DOM, and \DOMNodeList is
@ -97,7 +97,7 @@ if (version_compare(\PHP_VERSION, '8.0', '>=')) {
}
} else {
trait ParentNode {
public function __get_childElementCount(): int {
protected function __get_childElementCount(): int {
# The childElementCount getter steps are to return the number of children of
# this that are elements.
$count = 0;
@ -110,7 +110,7 @@ if (version_compare(\PHP_VERSION, '8.0', '>=')) {
return $count;
}
public function __get_children(): \DOMNodeList {
protected function __get_children(): \DOMNodeList {
# The children getter steps are to return an HTMLCollection collection rooted at
# this matching only element children.
// DEVIATION: HTMLCollection doesn't exist in PHP's DOM, and \DOMNodeList is
@ -123,7 +123,7 @@ if (version_compare(\PHP_VERSION, '8.0', '>=')) {
return $document->xpath->query('//*', (!$isDocument) ? $this : null);
}
public function __get_firstElementChild(): Element {
protected function __get_firstElementChild(): Element {
# The firstElementChild getter steps are to return the first child that is an
# element; otherwise null.
foreach ($this->childNodes as $child) {
@ -134,7 +134,7 @@ if (version_compare(\PHP_VERSION, '8.0', '>=')) {
return null;
}
public function __get_lastElementChild(): Element {
protected function __get_lastElementChild(): Element {
# The lastElementChild getter steps are to return the last child that is an
# element; otherwise null.
for ($i = $this->childNodes->length - 1; $i >= 0; $i--) {
@ -191,14 +191,14 @@ if (version_compare(\PHP_VERSION, '8.0', '>=')) {
}
# 5. Remove all parent’s children, in tree order, with the suppress observers
# flag set.
// DEVIATION: There is no scripting in this implementation, so cannnot set
// DEVIATION: There is no scripting in this implementation so cannnot set
// suppress observers flag.
while ($this->hasChildNodes()) {
$this->removeChild($this->firstChild);
}
# 6. If node is non-null, then insert node into parent before null with the
# suppress observers flag set.
// DEVIATION: There is no scripting in this implementation, so cannnot set
// DEVIATION: There is no scripting in this implementation so cannnot set
// suppress observers flag.
if ($node !== null) {
$this->appendChild($node);
@ -220,20 +220,18 @@ if (version_compare(\PHP_VERSION, '8.0', '>=')) {
// through them again to append. Let's optimize this a wee bit, shall we?
$document = ($this instanceof Document) ? $this : $this->ownerDocument;
$node = ($node->length > 1) ? $document->createDocumentFragment() : null;
foreach ($nodes as &$n) {
foreach ($nodes as $n) {
// Can't do union types until PHP 8... OTL
if (!$n instanceof \DOMNode && !is_string($n)) {
trigger_error(sprintf("Uncaught TypeError: %s::%s(): Argument #1 (\$%s) must be of type \DOMNode|string, %s given", __CLASS__, __METHOD__, 'nodes', gettype($n)));
throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'nodes', '\DOMNode|string', gettype($n));
}
if (is_string($n)) {
$n = $this->ownerDocument->createTextNode($n);
}
$nn = (!is_string($n)) ? $n : $this->ownerDocument->createTextNode($n);
if ($node !== null) {
$node->appendChild($n);
$node->appendChild($nn);
} else {
$node = $n;
$node = $nn;
}
}

Loading…
Cancel
Save