Element
class Element : Node, IElement, INode, IEventTarget, IMarkupFormattable, IParentNode, IChildNode, INonDocumentTypeChildNode, IElementCssInlineStyle
Represents an element node.
using AngleSharp.Dom.Collections;
using AngleSharp.Dom.Css;
using AngleSharp.Dom.Events;
using AngleSharp.Extensions;
using AngleSharp.Html;
using AngleSharp.Parser.Css;
using AngleSharp.Services.Styling;
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace AngleSharp.Dom
{
[DebuggerStepThrough]
internal class Element : Node, IElement, INode, IEventTarget, IMarkupFormattable, IParentNode, IChildNode, INonDocumentTypeChildNode, IElementCssInlineStyle
{
private static readonly ConditionalWeakTable<Element, IShadowRoot> shadowRoots = new ConditionalWeakTable<Element, IShadowRoot>();
private readonly NamedNodeMap _attributes;
private readonly string _namespace;
private readonly string _prefix;
private readonly string _localName;
private HtmlElementCollection _elements;
private ICssStyleDeclaration _style;
private TokenList _classList;
internal NamedNodeMap Attributes => _attributes;
public ICssStyleDeclaration Style => _style ?? (_style = CreateStyle());
public IElement AssignedSlot {
get {
IElement parentElement = base.ParentElement;
if (parentElement.IsShadow()) {
IShadowRoot shadowRoot = parentElement.ShadowRoot;
return shadowRoot.GetAssignedSlot(Slot);
}
return null;
}
}
public string Slot {
get {
return this.GetOwnAttribute(AttributeNames.Slot);
}
set {
this.SetOwnAttribute(AttributeNames.Slot, value);
}
}
public IShadowRoot ShadowRoot {
get {
IShadowRoot value = null;
shadowRoots.TryGetValue(this, out value);
return value;
}
}
public string Prefix => _prefix;
public string LocalName => _localName;
public string NamespaceUri => _namespace;
public override string TextContent {
get {
StringBuilder stringBuilder = Pool.NewStringBuilder();
foreach (IText item in this.GetDescendants().OfType<IText>()) {
stringBuilder.Append(item.Data);
}
return stringBuilder.ToPool();
}
set {
TextNode node = (!string.IsNullOrEmpty(value)) ? new TextNode(base.Owner, value) : null;
ReplaceAll(node, false);
}
}
public ITokenList ClassList {
get {
if (_classList == null) {
_classList = new TokenList(this.GetOwnAttribute(AttributeNames.Class));
CreateBindings(_classList, AttributeNames.Class);
}
return _classList;
}
}
public string ClassName {
get {
return this.GetOwnAttribute(AttributeNames.Class);
}
set {
this.SetOwnAttribute(AttributeNames.Class, value);
}
}
public string Id {
get {
return this.GetOwnAttribute(AttributeNames.Id);
}
set {
this.SetOwnAttribute(AttributeNames.Id, value);
}
}
public string TagName => base.NodeName;
public IElement PreviousElementSibling {
get {
Node parent = base.Parent;
if (parent != null) {
bool flag = false;
for (int num = parent.ChildNodes.Length - 1; num >= 0; num--) {
if (object.ReferenceEquals(parent.ChildNodes[num], this))
flag = true;
else if (flag && parent.ChildNodes[num] is IElement) {
return (IElement)parent.ChildNodes[num];
}
}
}
return null;
}
}
public IElement NextElementSibling {
get {
Node parent = base.Parent;
if (parent != null) {
int length = parent.ChildNodes.Length;
bool flag = false;
for (int i = 0; i < length; i++) {
if (object.ReferenceEquals(parent.ChildNodes[i], this))
flag = true;
else if (flag && parent.ChildNodes[i] is IElement) {
return (IElement)parent.ChildNodes[i];
}
}
}
return null;
}
}
public int ChildElementCount {
get {
NodeList childNodes = base.ChildNodes;
int length = childNodes.Length;
int num = 0;
for (int i = 0; i < length; i++) {
if (childNodes[i].NodeType == NodeType.Element)
num++;
}
return num;
}
}
public IHtmlCollection<IElement> Children => _elements ?? (_elements = new HtmlElementCollection(this, false, null));
public IElement FirstElementChild {
get {
NodeList childNodes = base.ChildNodes;
int length = childNodes.Length;
for (int i = 0; i < length; i++) {
IElement element = childNodes[i] as IElement;
if (element != null)
return element;
}
return null;
}
}
public IElement LastElementChild {
get {
NodeList childNodes = base.ChildNodes;
for (int num = childNodes.Length - 1; num >= 0; num--) {
IElement element = childNodes[num] as IElement;
if (element != null)
return element;
}
return null;
}
}
public string InnerHtml {
get {
return base.ChildNodes.ToHtml(HtmlMarkupFormatter.Instance);
}
set {
ReplaceAll(new DocumentFragment(this, value), false);
}
}
public string OuterHtml {
get {
return ToHtml(HtmlMarkupFormatter.Instance);
}
set {
Node parent = base.Parent;
if (parent == null)
throw new DomException(DomError.NotSupported);
Document owner = base.Owner;
if (owner != null && object.ReferenceEquals(owner.DocumentElement, this))
throw new DomException(DomError.NoModificationAllowed);
parent.InsertChild(parent.IndexOf(this), new DocumentFragment(this, value));
parent.RemoveChild(this);
}
}
INamedNodeMap IElement.Attributes {
get {
return _attributes;
}
}
public bool IsFocused {
get {
Document owner = base.Owner;
if (owner == null)
return false;
return object.ReferenceEquals(owner.FocusElement, this);
}
protected set {
Document owner = base.Owner;
if (owner != null) {
if (value) {
owner.SetFocus(this);
this.Fire(delegate(FocusEvent m) {
m.Init(EventNames.Focus, false, false);
}, null);
} else {
owner.SetFocus(null);
this.Fire(delegate(FocusEvent m) {
m.Init(EventNames.Blur, false, false);
}, null);
}
}
}
}
public Element(Document owner, string localName, string prefix, string namespaceUri, NodeFlags flags = NodeFlags.None)
: this(owner, (prefix != null) ? (prefix + ":" + localName) : localName, localName, prefix, namespaceUri, flags)
{
}
public Element(Document owner, string name, string localName, string prefix, string namespaceUri, NodeFlags flags = NodeFlags.None)
: base(owner, name, NodeType.Element, flags)
{
_localName = localName;
_prefix = prefix;
_namespace = namespaceUri;
_attributes = new NamedNodeMap(this);
}
public IShadowRoot AttachShadow(ShadowRootMode mode = ShadowRootMode.Open)
{
if (TagNames.AllNoShadowRoot.Contains(_localName))
throw new DomException(DomError.NotSupported);
if (ShadowRoot != null)
throw new DomException(DomError.InvalidState);
ShadowRoot shadowRoot = new ShadowRoot(this, mode);
shadowRoots.Add(this, shadowRoot);
return shadowRoot;
}
public IElement QuerySelector(string selectors)
{
return base.ChildNodes.QuerySelector(selectors);
}
public IHtmlCollection<IElement> QuerySelectorAll(string selectors)
{
return base.ChildNodes.QuerySelectorAll(selectors);
}
public IHtmlCollection<IElement> GetElementsByClassName(string classNames)
{
return base.ChildNodes.GetElementsByClassName(classNames);
}
public IHtmlCollection<IElement> GetElementsByTagName(string tagName)
{
return base.ChildNodes.GetElementsByTagName(tagName);
}
public IHtmlCollection<IElement> GetElementsByTagNameNS(string namespaceURI, string tagName)
{
return base.ChildNodes.GetElementsByTagName(namespaceURI, tagName);
}
public bool Matches(string selectors)
{
return CssParser.Default.ParseSelector(selectors).Match(this);
}
public override INode Clone(bool deep = true)
{
Element element = new Element(base.Owner, LocalName, _prefix, _namespace, base.Flags);
Node.CopyProperties(this, element, deep);
CopyAttributes(this, element);
return element;
}
public IPseudoElement Pseudo(string pseudoElement)
{
return PseudoElement.Create(this, pseudoElement);
}
public bool HasAttribute(string name)
{
if (_namespace.Is(NamespaceNames.HtmlUri))
name = name.ToLowerInvariant();
return _attributes.GetNamedItem(name) != null;
}
public bool HasAttribute(string namespaceUri, string localName)
{
if (string.IsNullOrEmpty(namespaceUri))
namespaceUri = null;
return _attributes.GetNamedItem(namespaceUri, localName) != null;
}
public string GetAttribute(string name)
{
if (_namespace.Is(NamespaceNames.HtmlUri))
name = name.ToLower();
return _attributes.GetNamedItem(name)?.Value;
}
public string GetAttribute(string namespaceUri, string localName)
{
if (string.IsNullOrEmpty(namespaceUri))
namespaceUri = null;
return _attributes.GetNamedItem(namespaceUri, localName)?.Value;
}
public void SetAttribute(string name, string value)
{
if (value != null) {
if (!name.IsXmlName())
throw new DomException(DomError.InvalidCharacter);
if (_namespace.Is(NamespaceNames.HtmlUri))
name = name.ToLowerInvariant();
this.SetOwnAttribute(name, value);
} else
RemoveAttribute(name);
}
public void SetAttribute(string namespaceUri, string name, string value)
{
if (value != null) {
string prefix = null;
string localName = null;
Node.GetPrefixAndLocalName(name, ref namespaceUri, out prefix, out localName);
_attributes.SetNamedItem(new Attr(prefix, localName, value, namespaceUri));
} else
RemoveAttribute(namespaceUri, name);
}
public void RemoveAttribute(string name)
{
if (_namespace.Is(NamespaceNames.HtmlUri))
name = name.ToLower();
_attributes.RemoveNamedItemOrDefault(name);
}
public void RemoveAttribute(string namespaceUri, string localName)
{
if (string.IsNullOrEmpty(namespaceUri))
namespaceUri = null;
_attributes.RemoveNamedItemOrDefault(namespaceUri, localName);
}
public void Prepend(params INode[] nodes)
{
this.PrependNodes(nodes);
}
public void Append(params INode[] nodes)
{
this.AppendNodes(nodes);
}
public override bool Equals(INode otherNode)
{
IElement element = otherNode as IElement;
if (element != null) {
if (NamespaceUri.Is(element.NamespaceUri) && _attributes.AreEqual(element.Attributes))
return base.Equals(otherNode);
return false;
}
return false;
}
public void Before(params INode[] nodes)
{
this.InsertBefore(nodes);
}
public void After(params INode[] nodes)
{
this.InsertAfter(nodes);
}
public void Replace(params INode[] nodes)
{
this.ReplaceWith(nodes);
}
public void Remove()
{
this.RemoveFromParent();
}
public void Insert(AdjacentPosition position, string html)
{
Element context = (position == AdjacentPosition.BeforeBegin || position == AdjacentPosition.AfterEnd) ? this : (base.Parent as Element);
DocumentFragment documentFragment = new DocumentFragment(context, html);
switch (position) {
case AdjacentPosition.BeforeBegin:
base.Parent.InsertBefore(documentFragment, this);
break;
case AdjacentPosition.AfterEnd:
base.Parent.InsertChild(base.Parent.IndexOf(this) + 1, documentFragment);
break;
case AdjacentPosition.AfterBegin:
InsertChild(0, documentFragment);
break;
case AdjacentPosition.BeforeEnd:
AppendChild(documentFragment);
break;
}
}
public override string ToHtml(IMarkupFormatter formatter)
{
bool flag = base.Flags.HasFlag(NodeFlags.SelfClosing);
string str = formatter.OpenTag(this, flag);
string str2 = string.Empty;
if (!flag) {
StringBuilder stringBuilder = Pool.NewStringBuilder();
if (base.Flags.HasFlag(NodeFlags.LineTolerance) && base.FirstChild is IText) {
IText text = (IText)base.FirstChild;
if (text.Data.Has('\n', 0))
stringBuilder.Append('\n');
}
foreach (INode childNode in base.ChildNodes) {
stringBuilder.Append(childNode.ToHtml(formatter));
}
str2 = stringBuilder.ToPool();
}
string str3 = formatter.CloseTag(this, flag);
return str + str2 + str3;
}
internal virtual void SetupElement()
{
}
internal void AttributeChanged(string localName, string namespaceUri, string oldValue)
{
base.Owner.QueueMutation(MutationRecord.Attributes(this, localName, namespaceUri, oldValue));
}
protected ICssStyleDeclaration CreateStyle()
{
if (_attributes.HasHandler(AttributeNames.Style)) {
IConfiguration options = base.Owner.Options;
ICssStyleEngine cssStyleEngine = options.GetCssStyleEngine();
if (cssStyleEngine != null) {
string ownAttribute = this.GetOwnAttribute(AttributeNames.Style);
StyleOptions styleOptions = new StyleOptions();
styleOptions.Element = this;
styleOptions.Configuration = options;
StyleOptions options2 = styleOptions;
ICssStyleDeclaration cssStyleDeclaration = cssStyleEngine.ParseDeclaration(ownAttribute, options2);
IBindable bindable = cssStyleDeclaration as IBindable;
if (bindable != null)
bindable.Changed += delegate(string value) {
UpdateAttribute(AttributeNames.Style, value);
};
return cssStyleDeclaration;
}
}
return null;
}
protected void CreateBindings(IBindable bindable, string attributeName)
{
bindable.Changed += delegate(string value) {
UpdateAttribute(attributeName, value);
};
RegisterAttributeObserver(attributeName, delegate(string value) {
bindable.Update(value);
});
}
protected void UpdateStyle(string value)
{
IBindable bindable = _style as IBindable;
if (string.IsNullOrEmpty(value))
RemoveAttribute(AttributeNames.Style);
bindable?.Update(value);
}
protected void UpdateAttribute(string name, string value)
{
Action<string> handler = _attributes.RemoveHandler(name);
this.SetOwnAttribute(name, value);
_attributes.SetHandler(name, handler);
}
protected sealed override string LocateNamespace(string prefix)
{
return ElementExtensions.LocateNamespace(this, prefix);
}
protected sealed override string LocatePrefix(string namespaceUri)
{
return ElementExtensions.LocatePrefix(this, namespaceUri);
}
protected static void CopyAttributes(Element source, Element target)
{
foreach (IAttr attribute in source._attributes) {
Attr attr = new Attr(attribute.Prefix, attribute.LocalName, attribute.Value, attribute.NamespaceUri);
target._attributes.FastAddItem(attr);
}
}
protected void RegisterAttributeObserver(string name, Action<string> callback)
{
_attributes.AddHandler(name, callback);
}
}
}