diff --git a/src/apiweb.zig b/src/apiweb.zig index cbf2ffa0..e45a9790 100644 --- a/src/apiweb.zig +++ b/src/apiweb.zig @@ -26,6 +26,7 @@ const Events = @import("events/event.zig"); const XHR = @import("xhr/xhr.zig"); const Storage = @import("storage/storage.zig"); const URL = @import("url/url.zig"); +const Iterators = @import("iterator/iterator.zig"); pub const HTMLDocument = @import("html/document.zig").HTMLDocument; @@ -38,6 +39,7 @@ pub const Interfaces = generate.Tuple(.{ XHR.Interfaces, Storage.Interfaces, URL.Interfaces, + Iterators.Interfaces, }); pub const UserContext = @import("user_context.zig").UserContext; diff --git a/src/dom/document.zig b/src/dom/document.zig index d58fcaed..11b29016 100644 --- a/src/dom/document.zig +++ b/src/dom/document.zig @@ -435,7 +435,11 @@ pub fn testExecFn( .{ .src = "document.querySelector(':root').nodeName", .ex = "HTML" }, .{ .src = "document.querySelectorAll('p').length", .ex = "2" }, - .{ .src = "document.querySelectorAll('.ok').item(0).id", .ex = "link" }, + .{ .src = + \\Array.from(document.querySelectorAll('#content > p#para-empty')) + \\.map(row => row.querySelector('span').textContent) + \\.length; + , .ex = "1" }, }; try checkCases(js_env, &querySelector); diff --git a/src/dom/dom.zig b/src/dom/dom.zig index 1ec36379..dc521bb6 100644 --- a/src/dom/dom.zig +++ b/src/dom/dom.zig @@ -23,7 +23,7 @@ const EventTarget = @import("event_target.zig").EventTarget; const DOMImplementation = @import("implementation.zig").DOMImplementation; const NamedNodeMap = @import("namednodemap.zig").NamedNodeMap; const DOMTokenList = @import("token_list.zig").DOMTokenList; -const NodeList = @import("nodelist.zig").NodeList; +const NodeList = @import("nodelist.zig"); const Nod = @import("node.zig"); const MutationObserver = @import("mutation_observer.zig"); @@ -33,7 +33,7 @@ pub const Interfaces = generate.Tuple(.{ DOMImplementation, NamedNodeMap, DOMTokenList, - NodeList, + NodeList.Interfaces, Nod.Node, Nod.Interfaces, MutationObserver.Interfaces, diff --git a/src/dom/nodelist.zig b/src/dom/nodelist.zig index 4c0fdd06..5be003de 100644 --- a/src/dom/nodelist.zig +++ b/src/dom/nodelist.zig @@ -25,14 +25,78 @@ const Callback = jsruntime.Callback; const CallbackResult = jsruntime.CallbackResult; const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; +const generate = @import("../generate.zig"); const NodeUnion = @import("node.zig").Union; const Node = @import("node.zig").Node; +const U32Iterator = @import("../iterator/iterator.zig").U32Iterator; + const log = std.log.scoped(.nodelist); const DOMException = @import("exceptions.zig").DOMException; +pub const Interfaces = generate.Tuple(.{ + NodeListIterator, + NodeList, +}); + +pub const NodeListIterator = struct { + pub const mem_guarantied = true; + + coll: *NodeList, + index: u32 = 0, + + pub const Return = struct { + value: ?NodeUnion, + done: bool, + }; + + pub fn _next(self: *NodeListIterator) !Return { + const e = try self.coll._item(self.index); + if (e == null) { + return Return{ + .value = null, + .done = true, + }; + } + + self.index += 1; + return Return{ + .value = e, + .done = false, + }; + } +}; + +pub const NodeListEntriesIterator = struct { + pub const mem_guarantied = true; + + coll: *NodeList, + index: u32 = 0, + + pub const Return = struct { + value: ?NodeUnion, + done: bool, + }; + + pub fn _next(self: *NodeListEntriesIterator) !Return { + const e = try self.coll._item(self.index); + if (e == null) { + return Return{ + .value = null, + .done = true, + }; + } + + self.index += 1; + return Return{ + .value = e, + .done = false, + }; + } +}; + // Nodelist is implemented in pure Zig b/c libdom's NodeList doesn't allow to // append nodes. // WEB IDL https://dom.spec.whatwg.org/#nodelist @@ -91,9 +155,38 @@ pub const NodeList = struct { } } - // TODO _symbol_iterator + pub fn _keys(self: *NodeList) U32Iterator { + log.debug("keys", .{}); + return .{ + .length = self.get_length(), + }; + } - // TODO implement postAttach + pub fn _values(self: *NodeList) NodeListIterator { + log.debug("values", .{}); + return .{ + .coll = self, + }; + } + + pub fn _symbol_iterator(self: *NodeList) NodeListIterator { + log.debug("symbol", .{}); + return self._values(); + } + + // TODO entries() https://developer.mozilla.org/en-US/docs/Web/API/NodeList/entries + + pub fn postAttach(self: *NodeList, alloc: std.mem.Allocator, js_obj: jsruntime.JSObject) !void { + const ln = self.get_length(); + var i: u32 = 0; + while (i < ln) { + defer i += 1; + const k = try std.fmt.allocPrint(alloc, "{d}", .{i}); + + const node = try self._item(i) orelse unreachable; + try js_obj.set(k, node); + } + } }; // Tests @@ -106,6 +199,7 @@ pub fn testExecFn( var childnodes = [_]Case{ .{ .src = "let list = document.getElementById('content').childNodes", .ex = "undefined" }, .{ .src = "list.length", .ex = "9" }, + .{ .src = "list[0].__proto__.constructor.name", .ex = "Text" }, .{ .src = \\let i = 0; \\list.forEach(function (n, idx) { diff --git a/src/iterator/iterator.zig b/src/iterator/iterator.zig new file mode 100644 index 00000000..e1dc9d3e --- /dev/null +++ b/src/iterator/iterator.zig @@ -0,0 +1,35 @@ +const std = @import("std"); + +const generate = @import("../generate.zig"); + +pub const Interfaces = generate.Tuple(.{ + U32Iterator, +}); + +pub const U32Iterator = struct { + pub const mem_guarantied = true; + + length: u32, + index: u32 = 0, + + pub const Return = struct { + value: u32, + done: bool, + }; + + pub fn _next(self: *U32Iterator) !Return { + const i = self.index; + if (i >= self.length) { + return Return{ + .value = 0, + .done = true, + }; + } + + self.index += 1; + return Return{ + .value = i, + .done = false, + }; + } +};