|
@@ -42,10 +42,27 @@ namespace Astralis {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
- /// Sets an attribute value
|
|
|
|
|
|
|
+ /// Checks if this element has a specific attribute
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- public void set_attribute(string name, string value) {
|
|
|
|
|
- xml_node->set_prop(name, value);
|
|
|
|
|
|
|
+ /// <param name="name">The attribute name to check</param>
|
|
|
|
|
+ /// <returns>True if the attribute exists, false otherwise</returns>
|
|
|
|
|
+ public bool has_attribute(string name) {
|
|
|
|
|
+ return xml_node->get_prop(name) != null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Sets an attribute value. If value is null, the attribute is removed.
|
|
|
|
|
+ /// For boolean attributes (e.g., "disabled", "checked"), pass null to remove
|
|
|
|
|
+ /// or use the attribute name as the value to set.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="name">The attribute name</param>
|
|
|
|
|
+ /// <param name="value">The attribute value, or null to remove the attribute</param>
|
|
|
|
|
+ public void set_attribute(string name, string? value) {
|
|
|
|
|
+ if (value == null) {
|
|
|
|
|
+ xml_node->unset_prop(name);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ xml_node->set_prop(name, value);
|
|
|
|
|
+ }
|
|
|
document.update(this);
|
|
document.update(this);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -386,15 +403,9 @@ namespace Astralis {
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Gets all child elements of this node
|
|
/// Gets all child elements of this node
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
- public MarkupNodeList children {
|
|
|
|
|
|
|
+ public Enumerable<MarkupNode> children {
|
|
|
owned get {
|
|
owned get {
|
|
|
- var list = new MarkupNodeList(document);
|
|
|
|
|
- for (var child = xml_node->children; child != null; child = child->next) {
|
|
|
|
|
- if (child->type == ElementType.ELEMENT_NODE) {
|
|
|
|
|
- list.add_node(child);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return list;
|
|
|
|
|
|
|
+ return new ChildNodesEnumerable(document, xml_node);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -645,183 +656,4 @@ namespace Astralis {
|
|
|
get { return xml_node; }
|
|
get { return xml_node; }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// A list of HTML nodes supporting iteration and manipulation
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public class MarkupNodeList : Invercargill.Enumerable<MarkupNode> {
|
|
|
|
|
- private MarkupDocument document;
|
|
|
|
|
- private List<Xml.Node*> nodes;
|
|
|
|
|
-
|
|
|
|
|
- internal MarkupNodeList(MarkupDocument doc) {
|
|
|
|
|
- this.document = doc;
|
|
|
|
|
- this.nodes = new List<Xml.Node*>();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- internal void add_node(Xml.Node* node) {
|
|
|
|
|
- nodes.append(node);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Number of nodes in the list
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public int length {
|
|
|
|
|
- get { return (int)nodes.length(); }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Gets a node at the specified index
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public new MarkupNode? get(int index) {
|
|
|
|
|
- unowned List<Xml.Node*> item = nodes.nth((uint)index);
|
|
|
|
|
- if (item == null) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- return new MarkupNode(document, item.data);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Gets the first node, or null if empty
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public new MarkupNode? first {
|
|
|
|
|
- owned get {
|
|
|
|
|
- if (nodes.is_empty()) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- unowned List<Xml.Node*> item = nodes.first();
|
|
|
|
|
- return item != null ? new MarkupNode(document, item.data) : null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Gets the last node, or null if empty
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public new MarkupNode? last {
|
|
|
|
|
- owned get {
|
|
|
|
|
- if (nodes.is_empty()) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- unowned List<Xml.Node*> item = nodes.last();
|
|
|
|
|
- return item != null ? new MarkupNode(document, item.data) : null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Sets a property on all nodes in the list
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public void set_attribute_all(string name, string value) {
|
|
|
|
|
- foreach (var node in nodes) {
|
|
|
|
|
- node->set_prop(name, value);
|
|
|
|
|
- document.update(new MarkupNode(document, node));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Adds a class to all nodes in the list
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public void add_class_all(string class_name) {
|
|
|
|
|
- foreach (var node in nodes) {
|
|
|
|
|
- var wrapper = new MarkupNode(document, node);
|
|
|
|
|
- wrapper.add_class(class_name);
|
|
|
|
|
- // Note: add_class already emits update signal
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Removes a class from all nodes in the list
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public void remove_class_all(string class_name) {
|
|
|
|
|
- foreach (var node in nodes) {
|
|
|
|
|
- var wrapper = new MarkupNode(document, node);
|
|
|
|
|
- wrapper.remove_class(class_name);
|
|
|
|
|
- // Note: remove_class already emits update signal
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Sets text content on all nodes in the list
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public void set_text_all(string text) {
|
|
|
|
|
- foreach (var node in nodes) {
|
|
|
|
|
- node->set_content(text);
|
|
|
|
|
- document.update(new MarkupNode(document, node));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Removes all nodes in the list from the DOM
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public void remove_all() {
|
|
|
|
|
- // Collect parent nodes before removal for update signals
|
|
|
|
|
- var parents = new List<Xml.Node*>();
|
|
|
|
|
- foreach (var node in nodes) {
|
|
|
|
|
- var parent = node->parent;
|
|
|
|
|
- if (parent != null && parent->type != ElementType.DOCUMENT_NODE) {
|
|
|
|
|
- parents.append(parent);
|
|
|
|
|
- }
|
|
|
|
|
- node->unlink();
|
|
|
|
|
- delete node;
|
|
|
|
|
- }
|
|
|
|
|
- nodes = new List<Xml.Node*>();
|
|
|
|
|
-
|
|
|
|
|
- // Emit update for all affected parents
|
|
|
|
|
- foreach (var parent in parents) {
|
|
|
|
|
- document.update(new MarkupNode(document, parent));
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Gets information about this enumerable
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public override Invercargill.EnumerableInfo get_info() {
|
|
|
|
|
- return new Invercargill.EnumerableInfo.infer_ultimate(this, Invercargill.EnumerableCategory.IN_MEMORY);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Gets a tracker for iterating over the nodes
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public override Invercargill.Tracker<MarkupNode> get_tracker() {
|
|
|
|
|
- return new NodeTracker(this);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Peeks at the count without full enumeration
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- public override uint? peek_count() {
|
|
|
|
|
- return (uint)nodes.length();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// Tracker for iterating over MarkupNodeList
|
|
|
|
|
- /// </summary>
|
|
|
|
|
- private class NodeTracker : Invercargill.Tracker<MarkupNode> {
|
|
|
|
|
- private MarkupNodeList list;
|
|
|
|
|
- private unowned List<Xml.Node*>? current;
|
|
|
|
|
-
|
|
|
|
|
- internal NodeTracker(owned MarkupNodeList list) {
|
|
|
|
|
- this.list = list;
|
|
|
|
|
- this.current = null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public override MarkupNode get_next() {
|
|
|
|
|
- if (current == null) {
|
|
|
|
|
- current = list.nodes.first();
|
|
|
|
|
- } else {
|
|
|
|
|
- current = current.next;
|
|
|
|
|
- }
|
|
|
|
|
- // has_next() should be called before get_next()
|
|
|
|
|
- // Returning null here would violate the non-nullable contract
|
|
|
|
|
- assert(current != null);
|
|
|
|
|
- return new MarkupNode(list.document, current.data);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public override bool has_next() {
|
|
|
|
|
- if (current == null) {
|
|
|
|
|
- return !list.nodes.is_empty();
|
|
|
|
|
- }
|
|
|
|
|
- return current.next != null;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|