Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/browser/dom/character_data.zig
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ pub const CharacterData = struct {

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
Expand Down
17 changes: 3 additions & 14 deletions src/browser/dom/document.zig
Original file line number Diff line number Diff line change
Expand Up @@ -227,28 +227,17 @@ pub const Document = struct {
return css.querySelectorAll(allocator, parser.documentToNode(self), selector);
}

// TODO according with https://dom.spec.whatwg.org/#parentnode, the
// 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 {
pub fn _prepend(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
return Node.prepend(parser.documentToNode(self), nodes);
}

// TODO according with https://dom.spec.whatwg.org/#parentnode, the
// 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 {
pub fn _append(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
return Node.append(parser.documentToNode(self), nodes);
}

// TODO according with https://dom.spec.whatwg.org/#parentnode, the
// 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 {
pub fn _replaceChildren(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
return Node.replaceChildren(parser.documentToNode(self), nodes);
}

pub fn deinit(_: *parser.Document, _: std.mem.Allocator) void {}
};

const testing = @import("../../testing.zig");
Expand Down
49 changes: 37 additions & 12 deletions src/browser/dom/element.zig
Original file line number Diff line number Diff line change
Expand Up @@ -313,24 +313,25 @@ pub const Element = struct {
return css.querySelectorAll(state.arena, parser.elementToNode(self), selector);
}

// TODO according with https://dom.spec.whatwg.org/#parentnode, the
// 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 {
pub fn _prepend(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
return Node.prepend(parser.elementToNode(self), nodes);
}

// TODO according with https://dom.spec.whatwg.org/#parentnode, the
// 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 {
pub fn _append(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
return Node.append(parser.elementToNode(self), nodes);
}

// TODO according with https://dom.spec.whatwg.org/#parentnode, the
// function must accept either node or string.
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
pub fn _replaceChildren(self: *parser.Element, nodes: []const *parser.Node) !void {
pub fn _before(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
const ref_node = parser.elementToNode(self);
return Node.before(ref_node, nodes);
}

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);
}

Expand Down Expand Up @@ -529,4 +530,28 @@ test "Browser.DOM.Element" {
.{ "el.matches('#9000')", "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>" },
}, .{});
}
147 changes: 117 additions & 30 deletions src/browser/dom/node.zig
Original file line number Diff line number Diff line change
Expand Up @@ -337,65 +337,72 @@ pub const Node = struct {
// For now, it checks only if new nodes are not self.
// TODO implements the others contraints.
// see https://dom.spec.whatwg.org/#concept-node-tree
pub fn hierarchy(self: *parser.Node, nodes: []const *parser.Node) !bool {
if (nodes.len == 0) return true;

for (nodes) |node| if (self == node) return false;

pub fn hierarchy(self: *parser.Node, nodes: []const NodeOrText) bool {
for (nodes) |n| {
if (n.is(self)) {
return false;
}
}
return true;
}

// TODO according with https://dom.spec.whatwg.org/#parentnode, the
// function must accept either node or string.
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
pub fn prepend(self: *parser.Node, nodes: []const *parser.Node) !void {
if (nodes.len == 0) return;
pub fn prepend(self: *parser.Node, nodes: []const NodeOrText) !void {
if (nodes.len == 0) {
return;
}

// 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;

const first = try parser.nodeFirstChild(self);
if (first == null) {
if (try parser.nodeFirstChild(self)) |first| {
for (nodes) |node| {
_ = try parser.nodeAppendChild(self, node);
_ = try parser.nodeInsertBefore(self, try node.toNode(doc), first);
}
return;
}

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
// function must accept either node or string.
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
pub fn append(self: *parser.Node, nodes: []const *parser.Node) !void {
if (nodes.len == 0) return;
pub fn append(self: *parser.Node, nodes: []const NodeOrText) !void {
if (nodes.len == 0) {
return;
}

// 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| {
_ = try parser.nodeAppendChild(self, node);
_ = try parser.nodeAppendChild(self, try node.toNode(doc));
}
}

// TODO according with https://dom.spec.whatwg.org/#parentnode, the
// function must accept either node or string.
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
pub fn replaceChildren(self: *parser.Node, nodes: []const *parser.Node) !void {
if (nodes.len == 0) return;
pub fn replaceChildren(self: *parser.Node, nodes: []const NodeOrText) !void {
if (nodes.len == 0) {
return;
}

// check hierarchy
if (!try hierarchy(self, nodes)) return parser.DOMError.HierarchyRequest;
if (!hierarchy(self, nodes)) {
return parser.DOMError.HierarchyRequest;
}

// remove existing children
try removeChildren(self);

const doc = (try parser.nodeOwnerDocument(self)) orelse return;
// add new children
for (nodes) |node| {
_ = try parser.nodeAppendChild(self, node);
_ = try parser.nodeAppendChild(self, try node.toNode(doc));
}
}

Expand All @@ -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");
Expand Down