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.Collections.Generic;
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) || !text.Equals(_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 List<IAttr> _attributes;
private readonly Dictionary<string, Action<string>> _attributeHandlers;
private readonly string _namespace;
private readonly string _prefix;
private readonly string _localName;
private HtmlElementCollection _elements;
private TokenList _classList;
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);
}
}
IEnumerable<IAttr> IElement.Attributes {
get {
return _attributes;
}
}
internal List<IAttr> Attributes => _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 List<IAttr>();
_attributeHandlers = new Dictionary<string, Action<string>>();
}
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 == Namespaces.HtmlUri)
name = name.ToLowerInvariant();
return _attributes.Has(name);
}
public bool HasAttribute(string namespaceUri, string localName)
{
if (string.IsNullOrEmpty(namespaceUri))
namespaceUri = null;
return _attributes.Has(namespaceUri, localName);
}
public string GetAttribute(string name)
{
if (_namespace == Namespaces.HtmlUri)
name = name.ToLower();
return _attributes.Get(name)?.Value;
}
protected string GetOwnAttribute(string name)
{
return _attributes.Get(null, name)?.Value;
}
public string GetAttribute(string namespaceUri, string localName)
{
if (string.IsNullOrEmpty(namespaceUri))
namespaceUri = null;
return _attributes.Get(namespaceUri, localName)?.Value;
}
public void SetAttribute(string name, string value)
{
if (value != null) {
if (!name.IsXmlName())
throw new DomException(DomError.InvalidCharacter);
if (_namespace == Namespaces.HtmlUri)
name = name.ToLowerInvariant();
for (int i = 0; i < _attributes.Count; i++) {
if (_attributes[i].Prefix == null && _attributes[i].LocalName == name) {
_attributes[i].Value = value;
return;
}
}
_attributes.Add(new Attr(this, name, value));
AttributeChanged(name, null, null, false);
} else
RemoveAttribute(name);
}
protected void SetOwnAttribute(string name, string value)
{
for (int i = 0; i < _attributes.Count; i++) {
if (_attributes[i].LocalName == name && _attributes[i].NamespaceUri == null) {
_attributes[i].Value = value;
return;
}
}
_attributes.Add(new Attr(this, null, name, value, null));
AttributeChanged(name, null, null, false);
}
public void SetAttribute(string namespaceUri, string name, string value)
{
if (value != null) {
if (string.IsNullOrEmpty(namespaceUri))
namespaceUri = null;
if (!name.IsXmlName())
throw new DomException(DomError.InvalidCharacter);
if (!name.IsQualifiedName())
throw new DomException(DomError.Namespace);
int num = name.IndexOf(':');
string text = (num >= 0) ? name.Substring(0, num) : null;
string text2 = (num >= 0) ? name.Substring(num + 1) : name;
if (text != null && namespaceUri == null)
throw new DomException(DomError.Namespace);
if (text == Namespaces.XmlPrefix && namespaceUri != Namespaces.XmlUri)
throw new DomException(DomError.Namespace);
if ((name == Namespaces.XmlNsPrefix || text == Namespaces.XmlNsPrefix) && namespaceUri != Namespaces.XmlNsUri)
throw new DomException(DomError.Namespace);
if (namespaceUri == Namespaces.XmlNsUri && name != Namespaces.XmlNsPrefix && text != Namespaces.XmlNsPrefix)
throw new DomException(DomError.Namespace);
for (int i = 0; i < _attributes.Count; i++) {
if (_attributes[i].LocalName == text2 && _attributes[i].NamespaceUri == namespaceUri) {
_attributes[i].Value = value;
return;
}
}
_attributes.Add(new Attr(this, text, text2, value, namespaceUri));
AttributeChanged(text2, namespaceUri, null, false);
} else
RemoveAttribute(namespaceUri, name);
}
public void RemoveAttribute(string name)
{
if (_namespace == Namespaces.HtmlUri)
name = name.ToLower();
int num = 0;
while (true) {
if (num >= _attributes.Count)
return;
if (_attributes[num].Prefix == null && _attributes[num].LocalName == name)
break;
num++;
}
IAttr attr = _attributes[num];
_attributes.RemoveAt(num);
AttributeChanged(attr.LocalName, attr.NamespaceUri, attr.Value, false);
}
public void RemoveAttribute(string namespaceUri, string localName)
{
if (string.IsNullOrEmpty(namespaceUri))
namespaceUri = null;
int num = 0;
while (true) {
if (num >= _attributes.Count)
return;
if (_attributes[num].LocalName == localName && _attributes[num].NamespaceUri == namespaceUri)
break;
num++;
}
IAttr attr = _attributes[num];
_attributes.RemoveAt(num);
AttributeChanged(attr.LocalName, attr.NamespaceUri, attr.Value, false);
}
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 != element.NamespaceUri)
return false;
if (_attributes.Count != element.Attributes.Count())
return false;
int i;
for (i = 0; i < _attributes.Count; i++) {
if (!element.Attributes.Any(delegate(IAttr m) {
if (m.Name == _attributes[i].Name)
return m.Value == _attributes[i].Value;
return false;
}))
return false;
}
return base.Equals(otherNode);
}
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 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> value2 = null;
if (_attributeHandlers.TryGetValue(name, out value2))
_attributeHandlers.Remove(name);
SetOwnAttribute(name, value);
if (value2 != null)
_attributeHandlers.Add(name, value2);
}
internal void AttributeChanged(string localName, string namespaceUri, string oldValue, bool suppressMutationObservers = false)
{
Action<string> value = null;
if (namespaceUri == null && _attributeHandlers.TryGetValue(localName, out value)) {
IAttr attr = _attributes.Get(null, localName);
value(attr?.Value);
}
if (!suppressMutationObservers)
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)
{
for (int i = 0; i < source._attributes.Count; i++) {
target._attributes.Add(new Attr(target, source._attributes[i].Name, source._attributes[i].Value));
}
}
protected void RegisterAttributeObserver(string name, Action<string> callback)
{
Action<string> value = null;
value = ((!_attributeHandlers.TryGetValue(name, out value)) ? callback : ((Action<string>)Delegate.Combine(value, callback)));
_attributeHandlers[name] = value;
}
}
}