mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Add before and after functions
Element and CharData both have a before & after function. Also, changed the existing functions that took a Node but should take a Node or Text, i.e append, prepend and replaceChildren. These functions, along with the new before/after all take a new NodeOrText union.
This commit is contained in:
@@ -112,6 +112,16 @@ pub const CharacterData = struct {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn _before(self: *parser.CharacterData, nodes: []const Node.NodeOrText) !void {
|
||||||
|
const ref_node = parser.characterDataToNode(self);
|
||||||
|
return Node.before(ref_node, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _after(self: *parser.CharacterData, nodes: []const Node.NodeOrText) !void {
|
||||||
|
const ref_node = parser.characterDataToNode(self);
|
||||||
|
return Node.after(ref_node, nodes);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
|
|||||||
@@ -227,28 +227,17 @@ pub const Document = struct {
|
|||||||
return css.querySelectorAll(allocator, parser.documentToNode(self), selector);
|
return css.querySelectorAll(allocator, parser.documentToNode(self), selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn _prepend(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
|
||||||
pub fn _prepend(self: *parser.Document, nodes: []const *parser.Node) !void {
|
|
||||||
return Node.prepend(parser.documentToNode(self), nodes);
|
return Node.prepend(parser.documentToNode(self), nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn _append(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
|
||||||
pub fn _append(self: *parser.Document, nodes: []const *parser.Node) !void {
|
|
||||||
return Node.append(parser.documentToNode(self), nodes);
|
return Node.append(parser.documentToNode(self), nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn _replaceChildren(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
|
||||||
pub fn _replaceChildren(self: *parser.Document, nodes: []const *parser.Node) !void {
|
|
||||||
return Node.replaceChildren(parser.documentToNode(self), nodes);
|
return Node.replaceChildren(parser.documentToNode(self), nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(_: *parser.Document, _: std.mem.Allocator) void {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
const testing = @import("../../testing.zig");
|
||||||
|
|||||||
@@ -313,24 +313,25 @@ pub const Element = struct {
|
|||||||
return css.querySelectorAll(state.arena, parser.elementToNode(self), selector);
|
return css.querySelectorAll(state.arena, parser.elementToNode(self), selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn _prepend(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
|
||||||
pub fn _prepend(self: *parser.Element, nodes: []const *parser.Node) !void {
|
|
||||||
return Node.prepend(parser.elementToNode(self), nodes);
|
return Node.prepend(parser.elementToNode(self), nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn _append(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
|
||||||
pub fn _append(self: *parser.Element, nodes: []const *parser.Node) !void {
|
|
||||||
return Node.append(parser.elementToNode(self), nodes);
|
return Node.append(parser.elementToNode(self), nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn _before(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
const ref_node = parser.elementToNode(self);
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
return Node.before(ref_node, nodes);
|
||||||
pub fn _replaceChildren(self: *parser.Element, nodes: []const *parser.Node) !void {
|
}
|
||||||
|
|
||||||
|
pub fn _after(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
|
||||||
|
const ref_node = parser.elementToNode(self);
|
||||||
|
return Node.after(ref_node, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _replaceChildren(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
|
||||||
return Node.replaceChildren(parser.elementToNode(self), nodes);
|
return Node.replaceChildren(parser.elementToNode(self), nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,4 +530,28 @@ test "Browser.DOM.Element" {
|
|||||||
.{ "el.matches('#9000')", "false" },
|
.{ "el.matches('#9000')", "false" },
|
||||||
.{ "el.matches('.notok')", "false" },
|
.{ "el.matches('.notok')", "false" },
|
||||||
}, .{});
|
}, .{});
|
||||||
|
|
||||||
|
// before
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ "const before_container = document.createElement('div');", "undefined" },
|
||||||
|
.{ "document.append(before_container);", "undefined" },
|
||||||
|
.{ "const b1 = document.createElement('div');", "undefined" },
|
||||||
|
.{ "before_container.append(b1);", "undefined" },
|
||||||
|
|
||||||
|
.{ "const b1_a = document.createElement('p');", "undefined" },
|
||||||
|
.{ "b1.before(b1_a, 'over 9000');", "undefined" },
|
||||||
|
.{ "before_container.innerHTML", "<p></p>over 9000<div></div>" },
|
||||||
|
}, .{});
|
||||||
|
|
||||||
|
// after
|
||||||
|
try runner.testCases(&.{
|
||||||
|
.{ "const after_container = document.createElement('div');", "undefined" },
|
||||||
|
.{ "document.append(after_container);", "undefined" },
|
||||||
|
.{ "const a1 = document.createElement('div');", "undefined" },
|
||||||
|
.{ "after_container.append(a1);", "undefined" },
|
||||||
|
|
||||||
|
.{ "const a1_a = document.createElement('p');", "undefined" },
|
||||||
|
.{ "a1.after('over 9000', a1_a);", "undefined" },
|
||||||
|
.{ "after_container.innerHTML", "<div></div>over 9000<p></p>" },
|
||||||
|
}, .{});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -337,65 +337,72 @@ pub const Node = struct {
|
|||||||
// For now, it checks only if new nodes are not self.
|
// For now, it checks only if new nodes are not self.
|
||||||
// TODO implements the others contraints.
|
// TODO implements the others contraints.
|
||||||
// see https://dom.spec.whatwg.org/#concept-node-tree
|
// see https://dom.spec.whatwg.org/#concept-node-tree
|
||||||
pub fn hierarchy(self: *parser.Node, nodes: []const *parser.Node) !bool {
|
pub fn hierarchy(self: *parser.Node, nodes: []const NodeOrText) bool {
|
||||||
if (nodes.len == 0) return true;
|
for (nodes) |n| {
|
||||||
|
if (n.is(self)) {
|
||||||
for (nodes) |node| if (self == node) return false;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn prepend(self: *parser.Node, nodes: []const NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
if (nodes.len == 0) {
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
return;
|
||||||
pub fn prepend(self: *parser.Node, nodes: []const *parser.Node) !void {
|
}
|
||||||
if (nodes.len == 0) return;
|
|
||||||
|
|
||||||
// check hierarchy
|
// check hierarchy
|
||||||
if (!try hierarchy(self, nodes)) return parser.DOMError.HierarchyRequest;
|
if (!hierarchy(self, nodes)) {
|
||||||
|
return parser.DOMError.HierarchyRequest;
|
||||||
|
}
|
||||||
|
|
||||||
const first = try parser.nodeFirstChild(self);
|
const doc = (try parser.nodeOwnerDocument(self)) orelse return;
|
||||||
if (first == null) {
|
|
||||||
|
if (try parser.nodeFirstChild(self)) |first| {
|
||||||
for (nodes) |node| {
|
for (nodes) |node| {
|
||||||
_ = try parser.nodeAppendChild(self, node);
|
_ = try parser.nodeInsertBefore(self, try node.toNode(doc), first);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (nodes) |node| {
|
for (nodes) |node| {
|
||||||
_ = try parser.nodeInsertBefore(self, node, first.?);
|
_ = try parser.nodeAppendChild(self, try node.toNode(doc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn append(self: *parser.Node, nodes: []const NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
if (nodes.len == 0) {
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
return;
|
||||||
pub fn append(self: *parser.Node, nodes: []const *parser.Node) !void {
|
}
|
||||||
if (nodes.len == 0) return;
|
|
||||||
|
|
||||||
// check hierarchy
|
// check hierarchy
|
||||||
if (!try hierarchy(self, nodes)) return parser.DOMError.HierarchyRequest;
|
if (!hierarchy(self, nodes)) {
|
||||||
|
return parser.DOMError.HierarchyRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = (try parser.nodeOwnerDocument(self)) orelse return;
|
||||||
for (nodes) |node| {
|
for (nodes) |node| {
|
||||||
_ = try parser.nodeAppendChild(self, node);
|
_ = try parser.nodeAppendChild(self, try node.toNode(doc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO according with https://dom.spec.whatwg.org/#parentnode, the
|
pub fn replaceChildren(self: *parser.Node, nodes: []const NodeOrText) !void {
|
||||||
// function must accept either node or string.
|
if (nodes.len == 0) {
|
||||||
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
return;
|
||||||
pub fn replaceChildren(self: *parser.Node, nodes: []const *parser.Node) !void {
|
}
|
||||||
if (nodes.len == 0) return;
|
|
||||||
|
|
||||||
// check hierarchy
|
// check hierarchy
|
||||||
if (!try hierarchy(self, nodes)) return parser.DOMError.HierarchyRequest;
|
if (!hierarchy(self, nodes)) {
|
||||||
|
return parser.DOMError.HierarchyRequest;
|
||||||
|
}
|
||||||
|
|
||||||
// remove existing children
|
// remove existing children
|
||||||
try removeChildren(self);
|
try removeChildren(self);
|
||||||
|
|
||||||
|
const doc = (try parser.nodeOwnerDocument(self)) orelse return;
|
||||||
// add new children
|
// add new children
|
||||||
for (nodes) |node| {
|
for (nodes) |node| {
|
||||||
_ = try parser.nodeAppendChild(self, node);
|
_ = try parser.nodeAppendChild(self, try node.toNode(doc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +421,87 @@ pub const Node = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(_: *parser.Node, _: std.mem.Allocator) void {}
|
pub fn before(self: *parser.Node, nodes: []const NodeOrText) !void {
|
||||||
|
const parent = try parser.nodeParentNode(self) orelse return;
|
||||||
|
const doc = (try parser.nodeOwnerDocument(parent)) orelse return;
|
||||||
|
|
||||||
|
var sibling: ?*parser.Node = self;
|
||||||
|
// have to find the first sibling that isn't in nodes
|
||||||
|
CHECK: while (sibling) |s| {
|
||||||
|
for (nodes) |n| {
|
||||||
|
if (n.is(s)) {
|
||||||
|
sibling = try parser.nodePreviousSibling(s);
|
||||||
|
continue :CHECK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sibling == null) {
|
||||||
|
sibling = try parser.nodeFirstChild(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sibling) |ref_node| {
|
||||||
|
for (nodes) |node| {
|
||||||
|
_ = try parser.nodeInsertBefore(parent, try node.toNode(doc), ref_node);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Node.prepend(self, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn after(self: *parser.Node, nodes: []const NodeOrText) !void {
|
||||||
|
const parent = try parser.nodeParentNode(self) orelse return;
|
||||||
|
const doc = (try parser.nodeOwnerDocument(parent)) orelse return;
|
||||||
|
|
||||||
|
// have to find the first sibling that isn't in nodes
|
||||||
|
var sibling = try parser.nodeNextSibling(self);
|
||||||
|
CHECK: while (sibling) |s| {
|
||||||
|
for (nodes) |n| {
|
||||||
|
if (n.is(s)) {
|
||||||
|
sibling = try parser.nodeNextSibling(s);
|
||||||
|
continue :CHECK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sibling) |ref_node| {
|
||||||
|
for (nodes) |node| {
|
||||||
|
_ = try parser.nodeInsertBefore(parent, try node.toNode(doc), ref_node);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (nodes) |node| {
|
||||||
|
_ = try parser.nodeAppendChild(parent, try node.toNode(doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A lot of functions take either a node or text input.
|
||||||
|
// The text input is to be converted into a Text node.
|
||||||
|
pub const NodeOrText = union(enum) {
|
||||||
|
text: []const u8,
|
||||||
|
node: *parser.Node,
|
||||||
|
|
||||||
|
fn toNode(self: NodeOrText, doc: *parser.Document) !*parser.Node {
|
||||||
|
return switch (self) {
|
||||||
|
.node => |n| n,
|
||||||
|
.text => |txt| @ptrCast(try parser.documentCreateTextNode(doc, txt)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the node represented by the NodeOrText is the same as the
|
||||||
|
// given Node. Always false for text values as these represent as-of-yet
|
||||||
|
// created Text nodes.
|
||||||
|
fn is(self: NodeOrText, other: *parser.Node) bool {
|
||||||
|
return switch (self) {
|
||||||
|
.text => false,
|
||||||
|
.node => |n| n == other,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
const testing = @import("../../testing.zig");
|
||||||
|
|||||||
Reference in New Issue
Block a user