Element
class Element : Node, IElement, INode, IEventTarget, IMarkupFormattable, IParentNode, IChildNode, INonDocumentTypeChildNode
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.Text;
namespace AngleSharp.Dom
{
[DebuggerStepThrough]
internal class Element : Node, IElement, INode, IEventTarget, IMarkupFormattable, IParentNode, IChildNode, INonDocumentTypeChildNode
{
protected sealed class BoundLocation : ILocation, IUrlUtilities
{
private readonly Element _parent;
private readonly string _attributeName;
private Location _location;
private Url _baseUrl;
private string _value;
public Location Location {
get {
string text = _parent.GetOwnAttribute(_attributeName) ?? string.Empty;
Url baseUrl = _parent.BaseUrl;
if (_location == null || !baseUrl.Equals(_baseUrl) || !string.Equals(text, _value, StringComparison.Ordinal)) {
Url url = new Url(baseUrl, text);
_baseUrl = baseUrl;
_value = text;
_location = new Location(url);
}
return _location;
}
}
public string Href {
get {
return Location.Href;
}
set {
Assign(value);
}
}
public string Protocol {
get {
return Location.Protocol;
}
set {
Location.Protocol = value;
Reload();
}
}
public string Host {
get {
return Location.Host;
}
set {
Location.Host = value;
Reload();
}
}
public string HostName {
get {
return Location.HostName;
}
set {
Location.HostName = value;
Reload();
}
}
public string Port {
get {
return Location.Port;
}
set {
Location.Port = value;
Reload();
}
}
public string PathName {
get {
return Location.PathName;
}
set {
Location.PathName = value;
Reload();
}
}
public string Search {
get {
return Location.Search;
}
set {
Location.Search = value;
Reload();
}
}
public string Hash {
get {
return Location.Hash;
}
set {
Location.Hash = value;
Reload();
}
}
public string UserName {
get {
return Location.UserName;
}
set {
Location.UserName = value;
Reload();
}
}
public string Password {
get {
return Location.Password;
}
set {
Location.Password = value;
Reload();
}
}
public string Origin => Location.Origin;
public BoundLocation(Element parent, string attributeName)
{
_parent = parent;
_attributeName = attributeName;
}
public void Assign(string url)
{
_parent.SetOwnAttribute(_attributeName, url);
_location = Location;
}
public void Replace(string url)
{
Assign(url);
}
public void Reload()
{
Assign(Href);
}
}
private readonly NamedNodeMap _attributes;
private readonly string _namespace;
private readonly string _prefix;
private readonly string _localName;
private HtmlElementCollection _elements;
private TokenList _classList;
internal NamedNodeMap Attributes => _attributes;
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(GetOwnAttribute(AttributeNames.Class));
CreateBindings(_classList, AttributeNames.Class);
}
return _classList;
}
}
public string ClassName {
get {
return GetOwnAttribute(AttributeNames.Class);
}
set {
SetOwnAttribute(AttributeNames.Class, value);
}
}
public string Id {
get {
return GetOwnAttribute(AttributeNames.Id);
}
set {
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 (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 (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) {
if (base.Owner != null && base.Owner.DocumentElement == this)
throw new DomException(DomError.NoModificationAllowed);
parent.InsertChild(parent.IndexOf(this), new DocumentFragment(this, value));
parent.RemoveChild(this);
return;
}
throw new DomException(DomError.NotSupported);
}
}
INamedNodeMap IElement.Attributes {
get {
return _attributes;
}
}
public bool IsFocused {
get {
Document owner = base.Owner;
if (owner == null)
return false;
return 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 name, NodeFlags flags = NodeFlags.None)
: this(owner, name, null, null, flags)
{
}
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 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 (string.Equals(_namespace, Namespaces.HtmlUri, StringComparison.Ordinal))
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 (string.Equals(_namespace, Namespaces.HtmlUri, StringComparison.Ordinal))
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 (string.Equals(_namespace, Namespaces.HtmlUri, StringComparison.Ordinal))
name = name.ToLowerInvariant();
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 (string.Equals(_namespace, Namespaces.HtmlUri, StringComparison.Ordinal))
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 (string.Equals(NamespaceUri, element.NamespaceUri, StringComparison.Ordinal) && _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.Length > 0 && text.Data[0] == '\n')
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;
}
protected string GetOwnAttribute(string name)
{
return _attributes.GetNamedItem(null, name)?.Value;
}
protected void SetOwnAttribute(string name, string value)
{
_attributes.SetNamedItemWithNamespaceUri(new Attr(name, value));
}
protected ICssStyleDeclaration CreateStyle()
{
IConfiguration options = base.Owner.Options;
ICssStyleEngine cssStyleEngine = options.GetCssStyleEngine();
if (cssStyleEngine != null) {
string ownAttribute = GetOwnAttribute(AttributeNames.Style);
StyleOptions styleOptions = new StyleOptions();
styleOptions.Element = this;
styleOptions.Configuration = options;
StyleOptions options2 = styleOptions;
ICssStyleDeclaration cssStyleDeclaration = cssStyleEngine.ParseInline(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 UpdateAttribute(string name, string value)
{
Action<string> handler = _attributes.RemoveHandler(name);
SetOwnAttribute(name, value);
_attributes.SetHandler(name, handler);
}
internal void AttributeChanged(string localName, string namespaceUri, string oldValue)
{
base.Owner.QueueMutation(MutationRecord.Attributes(this, localName, namespaceUri, oldValue));
}
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);
}
}
}