diff --git a/src/browser/tests/element/html/select.html b/src/browser/tests/element/html/select.html
index ceb46c16..4c4e6ccd 100644
--- a/src/browser/tests/element/html/select.html
+++ b/src/browser/tests/element/html/select.html
@@ -158,6 +158,7 @@
{
const sel = $('#select1')
const opts = sel.options
+ testing.expectEqual(3, sel.length)
testing.expectEqual(3, opts.length)
testing.expectEqual('HTMLOptionsCollection', opts.constructor.name)
@@ -165,6 +166,9 @@
testing.expectEqual('val1', opts[0].value)
testing.expectEqual('val2', opts[1].value)
testing.expectEqual('val3', opts[2].value)
+ testing.expectEqual('val1', opts.item(0).value);
+ testing.expectEqual('val2', opts.item(1).value);
+ testing.expectEqual('val3', opts.item(2).value);
}
@@ -224,6 +228,12 @@
testing.expectEqual(2, opts.length)
testing.expectEqual('zero', opts[0].value)
testing.expectEqual('b', opts[1].value)
+
+ opts.add(opt1, 0)
+ testing.expectEqual(3, opts.length)
+ testing.expectEqual('a', opts[0].value)
+ testing.expectEqual('zero', opts[1].value)
+ testing.expectEqual('b', opts[2].value)
}
@@ -364,3 +374,16 @@
testing.expectTrue(select.outerHTML.includes('size="7"'))
}
+
+
+
diff --git a/src/browser/webapi/collections/HTMLOptionsCollection.zig b/src/browser/webapi/collections/HTMLOptionsCollection.zig
index 4c9d59c4..6a0cadc9 100644
--- a/src/browser/webapi/collections/HTMLOptionsCollection.zig
+++ b/src/browser/webapi/collections/HTMLOptionsCollection.zig
@@ -20,6 +20,7 @@ const std = @import("std");
const js = @import("../../js/js.zig");
const Page = @import("../../Page.zig");
+const Node = @import("../Node.zig");
const Element = @import("../Element.zig");
const HTMLCollection = @import("HTMLCollection.zig");
const NodeLive = @import("node_live.zig").NodeLive;
@@ -59,17 +60,28 @@ pub fn setSelectedIndex(self: *HTMLOptionsCollection, index: i32) !void {
const Option = @import("../element/html/Option.zig");
+const AddBeforeOption = union(enum) {
+ option: *Option,
+ index: u32,
+};
+
// Add a new option element
-pub fn add(self: *HTMLOptionsCollection, element: *Option, before: ?*Option, page: *Page) !void {
+pub fn add(self: *HTMLOptionsCollection, element: *Option, before_: ?AddBeforeOption, page: *Page) !void {
const select_node = self._select.asNode();
const element_node = element.asElement().asNode();
- if (before) |before_option| {
- const before_node = before_option.asElement().asNode();
- _ = try select_node.insertBefore(element_node, before_node, page);
- } else {
- _ = try select_node.appendChild(element_node, page);
+ var before_node: ?*Node = null;
+ if (before_) |before| {
+ switch (before) {
+ .index => |idx| {
+ if (self.getAtIndex(idx, page)) |el| {
+ before_node = el.asNode();
+ }
+ },
+ .option => |before_option| before_node = before_option.asNode(),
+ }
}
+ _ = try select_node.insertBefore(element_node, before_node, page);
}
// Remove an option element by index
diff --git a/src/browser/webapi/element/html/Option.zig b/src/browser/webapi/element/html/Option.zig
index b5718a1e..207f4aaf 100644
--- a/src/browser/webapi/element/html/Option.zig
+++ b/src/browser/webapi/element/html/Option.zig
@@ -28,7 +28,6 @@ const Option = @This();
_proto: *HtmlElement,
_value: ?[]const u8 = null,
-_text: ?[]const u8 = null,
_selected: bool = false,
_default_selected: bool = false,
_disabled: bool = false,
@@ -43,9 +42,15 @@ pub fn asNode(self: *Option) *Node {
return self.asElement().asNode();
}
-pub fn getValue(self: *const Option) []const u8 {
- // If value attribute exists, use that; otherwise use text content
- return self._value orelse self._text orelse "";
+pub fn getValue(self: *Option, page: *Page) []const u8 {
+ // If value attribute exists, use that; otherwise use text content (stripped)
+ if (self._value) |v| {
+ return v;
+ }
+
+ const node = self.asNode();
+ const text = node.getTextContentAlloc(page.call_arena) catch return "";
+ return std.mem.trim(u8, text, &std.ascii.whitespace);
}
pub fn setValue(self: *Option, value: []const u8, page: *Page) !void {
@@ -55,7 +60,9 @@ pub fn setValue(self: *Option, value: []const u8, page: *Page) !void {
}
pub fn getText(self: *const Option) []const u8 {
- return self._text orelse "";
+ const node: *Node = @constCast(self.asConstElement().asConstNode());
+ const allocator = std.heap.page_allocator; // TODO: use proper allocator
+ return node.getTextContentAlloc(allocator) catch "";
}
pub fn getSelected(self: *const Option) bool {
@@ -112,8 +119,6 @@ pub const JsApi = struct {
};
pub const Build = struct {
- const CData = @import("../../CData.zig");
-
pub fn created(node: *Node, _: *Page) !void {
var self = node.as(Option);
const element = self.asElement();
@@ -129,17 +134,6 @@ pub const Build = struct {
self._disabled = element.getAttributeSafe("disabled") != null;
}
- pub fn complete(node: *Node, _: *const Page) !void {
- var self = node.as(Option);
-
- // Get text content
- if (node.firstChild()) |child| {
- if (child.is(CData.Text)) |txt| {
- self._text = txt.getWholeText();
- }
- }
- }
-
pub fn attributeChange(element: *Element, name: []const u8, value: []const u8, _: *Page) !void {
const attribute = std.meta.stringToEnum(enum { value, selected }, name) orelse return;
const self = element.as(Option);
diff --git a/src/browser/webapi/element/html/Select.zig b/src/browser/webapi/element/html/Select.zig
index beb00ee5..5b3c0b9e 100644
--- a/src/browser/webapi/element/html/Select.zig
+++ b/src/browser/webapi/element/html/Select.zig
@@ -44,7 +44,7 @@ pub fn asConstNode(self: *const Select) *const Node {
return self.asConstElement().asConstNode();
}
-pub fn getValue(self: *Select) []const u8 {
+pub fn getValue(self: *Select, page: *Page) []const u8 {
// Return value of first selected option, or first option if none selected
var first_option: ?*Option = null;
var iter = self.asNode().childrenIterator();
@@ -54,25 +54,24 @@ pub fn getValue(self: *Select) []const u8 {
first_option = option;
}
if (option.getSelected()) {
- return option.getValue();
+ return option.getValue(page);
}
}
// No explicitly selected option, return first option's value
if (first_option) |opt| {
- return opt.getValue();
+ return opt.getValue(page);
}
return "";
}
pub fn setValue(self: *Select, value: []const u8, page: *Page) !void {
- _ = page;
// Find option with matching value and select it
// Note: This updates the current state (_selected), not the default state (attribute)
// Setting value always deselects all others, even for multiple selects
var iter = self.asNode().childrenIterator();
while (iter.next()) |child| {
const option = child.is(Option) orelse continue;
- option._selected = std.mem.eql(u8, option.getValue(), value);
+ option._selected = std.mem.eql(u8, option.getValue(page), value);
}
}
@@ -196,6 +195,17 @@ pub fn getOptions(self: *Select, page: *Page) !*collections.HTMLOptionsCollectio
});
}
+pub fn getLength(self: *Select) u32 {
+ var i: u32 = 0;
+ var it = self.asNode().childrenIterator();
+ while (it.next()) |child| {
+ if (child.is(Option) != null) {
+ i += 1;
+ }
+ }
+ return i;
+}
+
pub fn getSelectedOptions(self: *Select, page: *Page) !collections.NodeLive(.selected_options) {
return collections.NodeLive(.selected_options).init(null, self.asNode(), {}, page);
}
@@ -243,6 +253,7 @@ pub const JsApi = struct {
pub const selectedOptions = bridge.accessor(Select.getSelectedOptions, null, .{});
pub const form = bridge.accessor(Select.getForm, null, .{});
pub const size = bridge.accessor(Select.getSize, Select.setSize, .{});
+ pub const length = bridge.accessor(Select.getLength, null, .{});
};
pub const Build = struct {