diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig
index ee8ee01c..06e94083 100644
--- a/src/browser/ScriptManager.zig
+++ b/src/browser/ScriptManager.zig
@@ -684,7 +684,7 @@ const Script = struct {
fn errorCallback(ctx: *anyopaque, err: anyerror) void {
const self: *Script = @ptrCast(@alignCast(ctx));
log.warn(.http, "script fetch error", .{
- .err = err ,
+ .err = err,
.req = self.url,
.mode = self.mode,
.kind = self.kind,
diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig
index faa61a2a..7f67f3a0 100644
--- a/src/browser/js/bridge.zig
+++ b/src/browser/js/bridge.zig
@@ -288,6 +288,7 @@ pub const Iterator = struct {
const Opts = struct {
async: bool = false,
+ null_as_undefined: bool = false,
};
fn init(comptime T: type, comptime struct_or_func: anytype, comptime opts: Opts) Iterator {
diff --git a/src/browser/tests/element/class_list.html b/src/browser/tests/element/class_list.html
index 91a5d18d..d29d7612 100644
--- a/src/browser/tests/element/class_list.html
+++ b/src/browser/tests/element/class_list.html
@@ -332,3 +332,272 @@
testing.expectEqual('a c e b f', div.className);
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/collections.zig b/src/browser/webapi/collections.zig
index eead81b9..3e36e05f 100644
--- a/src/browser/webapi/collections.zig
+++ b/src/browser/webapi/collections.zig
@@ -34,6 +34,8 @@ pub fn registerTypes() []const type {
@import("collections/HTMLAllCollection.zig").Iterator,
HTMLOptionsCollection,
DOMTokenList,
- DOMTokenList.Iterator,
+ DOMTokenList.KeyIterator,
+ DOMTokenList.ValueIterator,
+ DOMTokenList.EntryIterator,
};
}
diff --git a/src/browser/webapi/collections/DOMTokenList.zig b/src/browser/webapi/collections/DOMTokenList.zig
index 67ba027f..b47ae78d 100644
--- a/src/browser/webapi/collections/DOMTokenList.zig
+++ b/src/browser/webapi/collections/DOMTokenList.zig
@@ -17,11 +17,12 @@
// along with this program. If not, see .
const std = @import("std");
+const log = @import("../../../log.zig");
const js = @import("../../js/js.zig");
+const Page = @import("../../Page.zig");
const Element = @import("../Element.zig");
const GenericIterator = @import("iterator.zig").Entry;
-const Page = @import("../../Page.zig");
pub const DOMTokenList = @This();
@@ -32,6 +33,10 @@ pub const DOMTokenList = @This();
_element: *Element,
_attribute_name: []const u8,
+pub const KeyIterator = GenericIterator(Iterator, "0");
+pub const ValueIterator = GenericIterator(Iterator, "1");
+pub const EntryIterator = GenericIterator(Iterator, null);
+
const Lookup = std.StringArrayHashMapUnmanaged(void);
const WHITESPACE = " \t\n\r\x0C";
@@ -158,21 +163,40 @@ pub fn setValue(self: *DOMTokenList, value: []const u8, page: *Page) !void {
try self._element.setAttribute(self._attribute_name, value, page);
}
-pub fn iterator(self: *const DOMTokenList, page: *Page) !*Iterator {
- return Iterator.init(.{ .list = self }, page);
+pub fn keys(self: *DOMTokenList, page: *Page) !*KeyIterator {
+ return .init(.{ .list = self }, page);
}
-pub const Iterator = GenericIterator(struct {
- index: usize = 0,
- list: *const DOMTokenList,
+pub fn values(self: *DOMTokenList, page: *Page) !*ValueIterator {
+ return .init(.{ .list = self }, page);
+}
- // TODO: the underlying list.iten is very inefficient!
- pub fn next(self: *@This(), page: *Page) !?[]const u8 {
- const index = self.index;
- self.index = index + 1;
- return self.list.item(index, page);
+pub fn entries(self: *DOMTokenList, page: *Page) !*EntryIterator {
+ return .init(.{ .list = self }, page);
+}
+
+pub fn forEach(self: *DOMTokenList, cb_: js.Function, js_this_: ?js.Object, page: *Page) !void {
+ const cb = if (js_this_) |js_this| try cb_.withThis(js_this) else cb_;
+
+ const allocator = page.call_arena;
+
+ var i: i32 = 0;
+ var seen: std.StringArrayHashMapUnmanaged(void) = .empty;
+
+ var it = std.mem.tokenizeAny(u8, self.getValue(), WHITESPACE);
+ while (it.next()) |token| {
+ const gop = try seen.getOrPut(allocator, token);
+ if (gop.found_existing) {
+ continue;
+ }
+ var result: js.Function.Result = undefined;
+ cb.tryCall(void, .{ token, i, self }, &result) catch {
+ log.debug(.js, "forEach callback", .{ .err = result.exception, .stack = result.stack, .source = "DOMTokenList" });
+ return;
+ };
+ i += 1;
}
-}, null);
+}
fn getTokens(self: *const DOMTokenList, page: *Page) !Lookup {
const value = self.getValue();
@@ -205,6 +229,20 @@ fn updateAttribute(self: *DOMTokenList, tokens: Lookup, page: *Page) !void {
try self._element.setAttribute(self._attribute_name, joined, page);
}
+const Iterator = struct {
+ index: u32 = 0,
+ list: *DOMTokenList,
+
+ const Entry = struct { u32, []const u8 };
+
+ pub fn next(self: *Iterator, page: *Page) !?Entry {
+ const index = self.index;
+ const node = try self.list.item(index, page) orelse return null;
+ self.index = index + 1;
+ return .{ index, node };
+ }
+};
+
pub const JsApi = struct {
pub const bridge = js.Bridge(DOMTokenList);
@@ -229,6 +267,11 @@ pub const JsApi = struct {
pub const toggle = bridge.function(DOMTokenList.toggle, .{ .dom_exception = true });
pub const replace = bridge.function(DOMTokenList.replace, .{ .dom_exception = true });
pub const value = bridge.accessor(DOMTokenList.getValue, DOMTokenList.setValue, .{});
- pub const symbol_iterator = bridge.iterator(DOMTokenList.iterator, .{});
+ pub const toString = bridge.function(DOMTokenList.getValue, .{});
+ pub const keys = bridge.function(DOMTokenList.keys, .{});
+ pub const values = bridge.function(DOMTokenList.values, .{});
+ pub const entries = bridge.function(DOMTokenList.entries, .{});
+ pub const symbol_iterator = bridge.iterator(DOMTokenList.values, .{});
+ pub const forEach = bridge.function(DOMTokenList.forEach, .{});
pub const @"[]" = bridge.indexed(DOMTokenList.item, .{ .null_as_undefined = true });
};
diff --git a/src/browser/webapi/collections/iterator.zig b/src/browser/webapi/collections/iterator.zig
index 2c16ed85..0c063e84 100644
--- a/src/browser/webapi/collections/iterator.zig
+++ b/src/browser/webapi/collections/iterator.zig
@@ -21,11 +21,6 @@ const js = @import("../../js/js.zig");
const Page = @import("../../Page.zig");
pub fn Entry(comptime Inner: type, comptime field: ?[]const u8) type {
- // const InnerStruct = switch (@typeInfo(Inner)) {
- // .@"struct" => Inner,
- // .pointer => |ptr| ptr.child,
- // else => @compileError("invalid iterator type"),
- // };
const InnerStruct = Inner;
const R = reflect(InnerStruct, field);
@@ -68,7 +63,7 @@ pub fn Entry(comptime Inner: type, comptime field: ?[]const u8) type {
pub var class_id: bridge.ClassId = undefined;
};
- pub const next = bridge.function(Self.next, .{});
+ pub const next = bridge.function(Self.next, .{ .null_as_undefined = true });
pub const symbol_iterator = bridge.iterator(Self, .{});
};
};