Dustin Wilson
3 years ago
16 changed files with 259 additions and 430 deletions
@ -1,88 +0,0 @@ |
|||
<?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; |
|||
} |
|||
} |
@ -1,106 +0,0 @@ |
|||
<?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; |
|||
|
|||
// Node in the DOM spec is dirty. Many nodes which inherit from it inherit |
|||
// methods it cannot use which all check for this and throw exceptions. This is |
|||
// for nodes which DO have child nodes. |
|||
trait ContainerNode { |
|||
use Node; |
|||
|
|||
public function appendChild($node) { |
|||
$this->preInsertionValidity($node); |
|||
|
|||
$result = parent::appendChild($node); |
|||
if ($result !== false && $result instanceof TemplateElement) { |
|||
if ($result instanceof TemplateElement) { |
|||
ElementMap::set($result); |
|||
} |
|||
} |
|||
return $result; |
|||
} |
|||
|
|||
public function insertBefore($node, $child = null) { |
|||
$this->preInsertionValidity($node, $child); |
|||
|
|||
$result = parent::insertBefore($node, $child); |
|||
if ($result !== false) { |
|||
if ($result instanceof TemplateElement) { |
|||
ElementMap::set($result); |
|||
} |
|||
if ($child instanceof TemplateElement) { |
|||
ElementMap::delete($child); |
|||
} |
|||
} |
|||
return $result; |
|||
} |
|||
|
|||
public function removeChild($child) { |
|||
$result = parent::removeChild($child); |
|||
if ($result !== false && $result instanceof TemplateElement) { |
|||
ElementMap::delete($child); |
|||
} |
|||
return $result; |
|||
} |
|||
|
|||
public function replaceChild($node, $child) { |
|||
$result = parent::replaceChild($node, $child); |
|||
if ($result !== false) { |
|||
if ($result instanceof TemplateElement) { |
|||
ElementMap::set($child); |
|||
} |
|||
if ($child instanceof TemplateElement) { |
|||
ElementMap::delete($child); |
|||
} |
|||
} |
|||
return $result; |
|||
} |
|||
|
|||
|
|||
protected function preInsertionValidity(\DOMNode $node, ?\DOMNode $child = null) { |
|||
// "parent" in the spec comments below is $this |
|||
|
|||
# 1. If parent is not a Document, DocumentFragment, or Element node, then throw |
|||
# a "HierarchyRequestError" DOMException. |
|||
// Not necessary because they've been disabled and return hierarchy request |
|||
// errors in "leaf nodes". |
|||
|
|||
# 2. If node is a host-including inclusive ancestor of parent, then throw a |
|||
# "HierarchyRequestError" DOMException. |
|||
# |
|||
# An object A is a host-including inclusive ancestor of an object B, if either |
|||
# A is an inclusive ancestor of B, or if B’s root has a non-null host and A is a |
|||
# host-including inclusive ancestor of B’s root’s host. |
|||
// DEVIATION: The baseline for this library is PHP 7.1, and without |
|||
// WeakReferences we cannot add a host property to DocumentFragment to check |
|||
// against. |
|||
// This is handled just fine by PHP's DOM. |
|||
|
|||
# 3. If child is non-null and its parent is not parent, then throw a |
|||
# "NotFoundError" DOMException. |
|||
// This is handled just fine by PHP's DOM. |
|||
|
|||
# 4. If node is not a DocumentFragment, DocumentType, Element, Text, |
|||
# ProcessingInstruction, or Comment node, then throw a "HierarchyRequestError" |
|||
# DOMException. |
|||
if (!$node instanceof DocumentFragment && !$node instanceof \DOMDocumentType && !$node instanceof Element && !$node instanceof Text && !$node instanceof ProcessingInstruction && !$node instanceof Comment) { |
|||
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); |
|||
} |
|||
|
|||
# 5. If either node is a Text node and parent is a document, or node is a |
|||
# doctype and parent is not a document, then throw a "HierarchyRequestError" |
|||
# DOMException. |
|||
// Not necessary because they've been disabled and return hierarchy request |
|||
// errors in "leaf nodes". |
|||
|
|||
# 6. If parent is a document, and any of the statements below, switched on node, |
|||
# are true, then throw a "HierarchyRequestError" DOMException. |
|||
// Handled by the Document class. |
|||
} |
|||
} |
@ -0,0 +1,77 @@ |
|||
<?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; |
|||
|
|||
|
|||
if (version_compare(\PHP_VERSION, '8.0', '<')) { |
|||
/** |
|||
* Used for PHP7 installations to polyfill getters, setters, and methods that |
|||
* PHP's DOM handles natively in PHP8 |
|||
*/ |
|||
trait ParentNode { |
|||
protected function __get_childElementCount(): int { |
|||
# The childElementCount getter steps are to return the number of children of |
|||
# this that are elements. |
|||
$count = 0; |
|||
foreach ($this->childNodes as $child) { |
|||
if ($child instanceof Element) { |
|||
$count++; |
|||
} |
|||
} |
|||
|
|||
return $count; |
|||
} |
|||
|
|||
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) { |
|||
if ($child instanceof Element) { |
|||
return $child; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
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--) { |
|||
$child = $this->childNodes->item($i); |
|||
if ($child instanceof Element) { |
|||
return $child; |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
|
|||
public function append(...$nodes): void { |
|||
# The append(nodes) method steps are: |
|||
# 1. Let node be the result of converting nodes into a node given nodes and |
|||
# this’s node document. |
|||
$node = $this->convertNodesToNode($nodes); |
|||
# 2. Append node to this. |
|||
$this->appendChild($node); |
|||
} |
|||
|
|||
public function prepend(...$nodes): void { |
|||
# The prepend(nodes) method steps are: |
|||
# |
|||
# 1. Let node be the result of converting nodes into a node given nodes and |
|||
# this’s node document. |
|||
$node = $this->convertNodesToNode($nodes); |
|||
# 2. Pre-insert node into this before this’s first child. |
|||
$this->insertBefore($node, $this->firstChild); |
|||
} |
|||
} |
|||
} else { |
|||
trait ParentNodePolyfill {} |
|||
} |
Loading…
Reference in new issue