mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 16:28:58 +00:00
build FormData from optional form and optional submitter
This commit is contained in:
@@ -349,7 +349,7 @@
|
|||||||
testing.expectEqual([['b', '3']], acc);
|
testing.expectEqual([['b', '3']], acc);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- <script id=serialize>
|
<script id=serialize>
|
||||||
{
|
{
|
||||||
let form1 = $('#form1');
|
let form1 = $('#form1');
|
||||||
let submit1 = $('#s1');
|
let submit1 = $('#s1');
|
||||||
@@ -380,4 +380,249 @@
|
|||||||
testing.expectEqual(['mlt-2', 'tea'], acc[12]);
|
testing.expectEqual(['mlt-2', 'tea'], acc[12]);
|
||||||
testing.expectEqual(['s1', 's1-v'], acc[13]);
|
testing.expectEqual(['s1', 's1-v'], acc[13]);
|
||||||
}
|
}
|
||||||
</script> -->
|
</script>
|
||||||
|
|
||||||
|
<script id=submitterBehavior>
|
||||||
|
{
|
||||||
|
// Test that only the specified submitter is included in FormData
|
||||||
|
const form = document.createElement('form');
|
||||||
|
|
||||||
|
const input1 = document.createElement('input');
|
||||||
|
input1.name = 'field1';
|
||||||
|
input1.value = 'value1';
|
||||||
|
form.appendChild(input1);
|
||||||
|
|
||||||
|
const submit1 = document.createElement('input');
|
||||||
|
submit1.type = 'submit';
|
||||||
|
submit1.name = 'action';
|
||||||
|
submit1.value = 'save';
|
||||||
|
form.appendChild(submit1);
|
||||||
|
|
||||||
|
const submit2 = document.createElement('input');
|
||||||
|
submit2.type = 'submit';
|
||||||
|
submit2.name = 'action';
|
||||||
|
submit2.value = 'delete';
|
||||||
|
form.appendChild(submit2);
|
||||||
|
|
||||||
|
const submit3 = document.createElement('input');
|
||||||
|
submit3.type = 'submit';
|
||||||
|
submit3.name = 'action';
|
||||||
|
submit3.value = 'cancel';
|
||||||
|
form.appendChild(submit3);
|
||||||
|
|
||||||
|
// FormData with submit2 as submitter - should only include submit2
|
||||||
|
const fd = new FormData(form, submit2);
|
||||||
|
testing.expectEqual('value1', fd.get('field1'));
|
||||||
|
testing.expectEqual('delete', fd.get('action'));
|
||||||
|
testing.expectEqual(['delete'], fd.getAll('action'));
|
||||||
|
|
||||||
|
// FormData with no submitter - should not include any submit buttons
|
||||||
|
const fd2 = new FormData(form);
|
||||||
|
testing.expectEqual('value1', fd2.get('field1'));
|
||||||
|
testing.expectEqual(null, fd2.get('action'));
|
||||||
|
testing.expectEqual([], fd2.getAll('action'));
|
||||||
|
|
||||||
|
// FormData with submit1 as submitter
|
||||||
|
const fd3 = new FormData(form, submit1);
|
||||||
|
testing.expectEqual('value1', fd3.get('field1'));
|
||||||
|
testing.expectEqual('save', fd3.get('action'));
|
||||||
|
testing.expectEqual(['save'], fd3.getAll('action'));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=imageSubmitter>
|
||||||
|
{
|
||||||
|
// Test that image inputs add name.x and name.y coordinates
|
||||||
|
const form = document.createElement('form');
|
||||||
|
|
||||||
|
const input1 = document.createElement('input');
|
||||||
|
input1.name = 'username';
|
||||||
|
input1.value = 'alice';
|
||||||
|
form.appendChild(input1);
|
||||||
|
|
||||||
|
const image1 = document.createElement('input');
|
||||||
|
image1.type = 'image';
|
||||||
|
image1.name = 'submit';
|
||||||
|
image1.value = 'ignored'; // value is ignored for image inputs
|
||||||
|
form.appendChild(image1);
|
||||||
|
|
||||||
|
const fd = new FormData(form, image1);
|
||||||
|
testing.expectEqual('alice', fd.get('username'));
|
||||||
|
testing.expectEqual('0', fd.get('submit.x'));
|
||||||
|
testing.expectEqual('0', fd.get('submit.y'));
|
||||||
|
testing.expectEqual(null, fd.get('submit')); // name without .x/.y should not exist
|
||||||
|
|
||||||
|
// Verify order and that .x comes before .y
|
||||||
|
const entries = Array.from(fd.entries());
|
||||||
|
testing.expectEqual([['username', 'alice'], ['submit.x', '0'], ['submit.y', '0']], entries);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=multipleImagesWithSameName>
|
||||||
|
{
|
||||||
|
// Test that when multiple images share a name, only submitter's coordinates are included
|
||||||
|
const form = document.createElement('form');
|
||||||
|
|
||||||
|
const img1 = document.createElement('input');
|
||||||
|
img1.type = 'image';
|
||||||
|
img1.name = 'coords';
|
||||||
|
form.appendChild(img1);
|
||||||
|
|
||||||
|
const img2 = document.createElement('input');
|
||||||
|
img2.type = 'image';
|
||||||
|
img2.name = 'coords';
|
||||||
|
form.appendChild(img2);
|
||||||
|
|
||||||
|
const img3 = document.createElement('input');
|
||||||
|
img3.type = 'image';
|
||||||
|
img3.name = 'coords';
|
||||||
|
form.appendChild(img3);
|
||||||
|
|
||||||
|
// Only img2 should be included
|
||||||
|
const fd = new FormData(form, img2);
|
||||||
|
testing.expectEqual(['0'], fd.getAll('coords.x'));
|
||||||
|
testing.expectEqual(['0'], fd.getAll('coords.y'));
|
||||||
|
|
||||||
|
const entries = Array.from(fd.entries());
|
||||||
|
testing.expectEqual([['coords.x', '0'], ['coords.y', '0']], entries);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=buttonSubmitter>
|
||||||
|
{
|
||||||
|
// Test that <button> elements work as submitters
|
||||||
|
const form = document.createElement('form');
|
||||||
|
|
||||||
|
const input1 = document.createElement('input');
|
||||||
|
input1.name = 'data';
|
||||||
|
input1.value = 'test';
|
||||||
|
form.appendChild(input1);
|
||||||
|
|
||||||
|
const button1 = document.createElement('button');
|
||||||
|
button1.name = 'btn';
|
||||||
|
button1.value = 'first';
|
||||||
|
button1.type = 'submit';
|
||||||
|
form.appendChild(button1);
|
||||||
|
|
||||||
|
const button2 = document.createElement('button');
|
||||||
|
button2.name = 'btn';
|
||||||
|
button2.value = 'second';
|
||||||
|
button2.type = 'submit';
|
||||||
|
form.appendChild(button2);
|
||||||
|
|
||||||
|
// With button1 as submitter
|
||||||
|
const fd1 = new FormData(form, button1);
|
||||||
|
testing.expectEqual('test', fd1.get('data'));
|
||||||
|
testing.expectEqual('first', fd1.get('btn'));
|
||||||
|
testing.expectEqual(['first'], fd1.getAll('btn'));
|
||||||
|
|
||||||
|
// With button2 as submitter
|
||||||
|
const fd2 = new FormData(form, button2);
|
||||||
|
testing.expectEqual('test', fd2.get('data'));
|
||||||
|
testing.expectEqual('second', fd2.get('btn'));
|
||||||
|
testing.expectEqual(['second'], fd2.getAll('btn'));
|
||||||
|
|
||||||
|
// No submitter - no button included
|
||||||
|
const fd3 = new FormData(form);
|
||||||
|
testing.expectEqual('test', fd3.get('data'));
|
||||||
|
testing.expectEqual(null, fd3.get('btn'));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=mixedSubmitTypes>
|
||||||
|
{
|
||||||
|
// Test mix of input[type=submit], input[type=image], and button
|
||||||
|
const form = document.createElement('form');
|
||||||
|
|
||||||
|
const field = document.createElement('input');
|
||||||
|
field.name = 'name';
|
||||||
|
field.value = 'Bob';
|
||||||
|
form.appendChild(field);
|
||||||
|
|
||||||
|
const submit = document.createElement('input');
|
||||||
|
submit.type = 'submit';
|
||||||
|
submit.name = 'op';
|
||||||
|
submit.value = 'save';
|
||||||
|
form.appendChild(submit);
|
||||||
|
|
||||||
|
const image = document.createElement('input');
|
||||||
|
image.type = 'image';
|
||||||
|
image.name = 'map';
|
||||||
|
form.appendChild(image);
|
||||||
|
|
||||||
|
const button = document.createElement('button');
|
||||||
|
button.type = 'submit';
|
||||||
|
button.name = 'op';
|
||||||
|
button.value = 'delete';
|
||||||
|
form.appendChild(button);
|
||||||
|
|
||||||
|
// Using image as submitter - only image coordinates included
|
||||||
|
const fd1 = new FormData(form, image);
|
||||||
|
testing.expectEqual('Bob', fd1.get('name'));
|
||||||
|
testing.expectEqual('0', fd1.get('map.x'));
|
||||||
|
testing.expectEqual('0', fd1.get('map.y'));
|
||||||
|
testing.expectEqual(null, fd1.get('op'));
|
||||||
|
|
||||||
|
// Using submit as submitter
|
||||||
|
const fd2 = new FormData(form, submit);
|
||||||
|
testing.expectEqual('Bob', fd2.get('name'));
|
||||||
|
testing.expectEqual('save', fd2.get('op'));
|
||||||
|
testing.expectEqual(null, fd2.get('map.x'));
|
||||||
|
testing.expectEqual(null, fd2.get('map.y'));
|
||||||
|
|
||||||
|
// Using button as submitter
|
||||||
|
const fd3 = new FormData(form, button);
|
||||||
|
testing.expectEqual('Bob', fd3.get('name'));
|
||||||
|
testing.expectEqual('delete', fd3.get('op'));
|
||||||
|
testing.expectEqual(null, fd3.get('map.x'));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=submitterWithoutName>
|
||||||
|
{
|
||||||
|
// Test that submitter without name is not included
|
||||||
|
const form = document.createElement('form');
|
||||||
|
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.name = 'field';
|
||||||
|
input.value = 'data';
|
||||||
|
form.appendChild(input);
|
||||||
|
|
||||||
|
const submit = document.createElement('input');
|
||||||
|
submit.type = 'submit';
|
||||||
|
// no name attribute
|
||||||
|
submit.value = 'Submit';
|
||||||
|
form.appendChild(submit);
|
||||||
|
|
||||||
|
const fd = new FormData(form, submit);
|
||||||
|
testing.expectEqual('data', fd.get('field'));
|
||||||
|
// Should not have any submit-related entries
|
||||||
|
const entries = Array.from(fd.entries());
|
||||||
|
testing.expectEqual([['field', 'data']], entries);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=imageWithoutName>
|
||||||
|
{
|
||||||
|
// Test that image input without name still submits x and y coordinates
|
||||||
|
const form = document.createElement('form');
|
||||||
|
|
||||||
|
const input = document.createElement('input');
|
||||||
|
input.name = 'field';
|
||||||
|
input.value = 'data';
|
||||||
|
form.appendChild(input);
|
||||||
|
|
||||||
|
const image = document.createElement('input');
|
||||||
|
image.type = 'image';
|
||||||
|
// no name attribute
|
||||||
|
form.appendChild(image);
|
||||||
|
|
||||||
|
const fd = new FormData(form, image);
|
||||||
|
testing.expectEqual('data', fd.get('field'));
|
||||||
|
// Image without name submits plain 'x' and 'y' keys
|
||||||
|
testing.expectEqual('0', fd.get('x'));
|
||||||
|
testing.expectEqual('0', fd.get('y'));
|
||||||
|
const entries = Array.from(fd.entries());
|
||||||
|
testing.expectEqual([['field', 'data'], ['x', '0'], ['y', '0']], entries);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ pub fn getTagNameLower(self: *const Element) []const u8 {
|
|||||||
.slot => "slot",
|
.slot => "slot",
|
||||||
.style => "style",
|
.style => "style",
|
||||||
.template => "template",
|
.template => "template",
|
||||||
.text_area => "textarea",
|
.textarea => "textarea",
|
||||||
.title => "title",
|
.title => "title",
|
||||||
.ul => "ul",
|
.ul => "ul",
|
||||||
.unknown => |e| e._tag_name.str(),
|
.unknown => |e| e._tag_name.str(),
|
||||||
@@ -254,7 +254,7 @@ pub fn getTagNameSpec(self: *const Element, buf: []u8) []const u8 {
|
|||||||
.slot => "SLOT",
|
.slot => "SLOT",
|
||||||
.style => "STYLE",
|
.style => "STYLE",
|
||||||
.template => "TEMPLATE",
|
.template => "TEMPLATE",
|
||||||
.text_area => "TEXTAREA",
|
.textarea => "TEXTAREA",
|
||||||
.title => "TITLE",
|
.title => "TITLE",
|
||||||
.ul => "UL",
|
.ul => "UL",
|
||||||
.unknown => |e| switch (self._namespace) {
|
.unknown => |e| switch (self._namespace) {
|
||||||
@@ -1097,7 +1097,7 @@ pub fn getTag(self: *const Element) Tag {
|
|||||||
.slot => .slot,
|
.slot => .slot,
|
||||||
.option => .option,
|
.option => .option,
|
||||||
.template => .template,
|
.template => .template,
|
||||||
.text_area => .textarea,
|
.textarea => .textarea,
|
||||||
.input => .input,
|
.input => .input,
|
||||||
.link => .link,
|
.link => .link,
|
||||||
.meta => .meta,
|
.meta => .meta,
|
||||||
|
|||||||
@@ -194,6 +194,10 @@ pub fn NodeLive(comptime mode: Mode) type {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *Self) ?*Element {
|
||||||
|
return self.nextTw(&self._tw);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn nextTw(self: *Self, tw: *TW) ?*Element {
|
pub fn nextTw(self: *Self, tw: *TW) ?*Element {
|
||||||
while (tw.next()) |node| {
|
while (tw.next()) |node| {
|
||||||
if (self.matches(node)) {
|
if (self.matches(node)) {
|
||||||
@@ -297,7 +301,7 @@ pub fn NodeLive(comptime mode: Mode) type {
|
|||||||
if (el._type != .html) return false;
|
if (el._type != .html) return false;
|
||||||
const html = el._type.html;
|
const html = el._type.html;
|
||||||
return switch (html._type) {
|
return switch (html._type) {
|
||||||
.input, .button, .select, .text_area => true,
|
.input, .button, .select, .textarea => true,
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ pub const Type = union(enum) {
|
|||||||
slot: *Slot,
|
slot: *Slot,
|
||||||
style: *Style,
|
style: *Style,
|
||||||
template: *Template,
|
template: *Template,
|
||||||
text_area: *TextArea,
|
textarea: *TextArea,
|
||||||
title: *Title,
|
title: *Title,
|
||||||
ul: *UL,
|
ul: *UL,
|
||||||
unknown: *Unknown,
|
unknown: *Unknown,
|
||||||
@@ -156,7 +156,7 @@ pub fn className(self: *const HtmlElement) []const u8 {
|
|||||||
.slot => "[object HTMLSlotElement]",
|
.slot => "[object HTMLSlotElement]",
|
||||||
.style => "[object HTMLSyleElement]",
|
.style => "[object HTMLSyleElement]",
|
||||||
.template => "[object HTMLTemplateElement]",
|
.template => "[object HTMLTemplateElement]",
|
||||||
.text_area => "[object HTMLTextAreaElement]",
|
.textarea => "[object HTMLTextAreaElement]",
|
||||||
.title => "[object HTMLTitleElement]",
|
.title => "[object HTMLTitleElement]",
|
||||||
.ul => "[object HTMLULElement]",
|
.ul => "[object HTMLULElement]",
|
||||||
.unknown => "[object HTMLUnknownElement]",
|
.unknown => "[object HTMLUnknownElement]",
|
||||||
|
|||||||
@@ -58,6 +58,14 @@ pub fn setName(self: *Button, name: []const u8, page: *Page) !void {
|
|||||||
try self.asElement().setAttributeSafe("name", name, page);
|
try self.asElement().setAttributeSafe("name", name, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getValue(self: *const Button) []const u8 {
|
||||||
|
return self.asConstElement().getAttributeSafe("value") orelse "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setValue(self: *Button, value: []const u8, page: *Page) !void {
|
||||||
|
try self.asElement().setAttributeSafe("value", value, page);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getRequired(self: *const Button) bool {
|
pub fn getRequired(self: *const Button) bool {
|
||||||
return self.asConstElement().getAttributeSafe("required") != null;
|
return self.asConstElement().getAttributeSafe("required") != null;
|
||||||
}
|
}
|
||||||
@@ -107,6 +115,7 @@ pub const JsApi = struct {
|
|||||||
pub const name = bridge.accessor(Button.getName, Button.setName, .{});
|
pub const name = bridge.accessor(Button.getName, Button.setName, .{});
|
||||||
pub const required = bridge.accessor(Button.getRequired, Button.setRequired, .{});
|
pub const required = bridge.accessor(Button.getRequired, Button.setRequired, .{});
|
||||||
pub const form = bridge.accessor(Button.getForm, null, .{});
|
pub const form = bridge.accessor(Button.getForm, null, .{});
|
||||||
|
pub const value = bridge.accessor(Button.getValue, Button.setValue, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Build = struct {
|
pub const Build = struct {
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ const HtmlElement = @import("../Html.zig");
|
|||||||
const TreeWalker = @import("../../TreeWalker.zig");
|
const TreeWalker = @import("../../TreeWalker.zig");
|
||||||
const collections = @import("../../collections.zig");
|
const collections = @import("../../collections.zig");
|
||||||
|
|
||||||
const Input = @import("Input.zig");
|
pub const Input = @import("Input.zig");
|
||||||
const Button = @import("Button.zig");
|
pub const Button = @import("Button.zig");
|
||||||
const Select = @import("Select.zig");
|
pub const Select = @import("Select.zig");
|
||||||
const TextArea = @import("TextArea.zig");
|
pub const TextArea = @import("TextArea.zig");
|
||||||
|
|
||||||
const Form = @This();
|
const Form = @This();
|
||||||
_proto: *HtmlElement,
|
_proto: *HtmlElement,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const Element = @import("../../Element.zig");
|
|||||||
const HtmlElement = @import("../Html.zig");
|
const HtmlElement = @import("../Html.zig");
|
||||||
const collections = @import("../../collections.zig");
|
const collections = @import("../../collections.zig");
|
||||||
const Form = @import("Form.zig");
|
const Form = @import("Form.zig");
|
||||||
const Option = @import("Option.zig");
|
pub const Option = @import("Option.zig");
|
||||||
|
|
||||||
const Select = @This();
|
const Select = @This();
|
||||||
|
|
||||||
@@ -50,12 +50,16 @@ pub fn getValue(self: *Select, page: *Page) []const u8 {
|
|||||||
var iter = self.asNode().childrenIterator();
|
var iter = self.asNode().childrenIterator();
|
||||||
while (iter.next()) |child| {
|
while (iter.next()) |child| {
|
||||||
const option = child.is(Option) orelse continue;
|
const option = child.is(Option) orelse continue;
|
||||||
if (first_option == null) {
|
if (option.getDisabled()) {
|
||||||
first_option = option;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option.getSelected()) {
|
if (option.getSelected()) {
|
||||||
return option.getValue(page);
|
return option.getValue(page);
|
||||||
}
|
}
|
||||||
|
if (first_option == null) {
|
||||||
|
first_option = option;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// No explicitly selected option, return first option's value
|
// No explicitly selected option, return first option's value
|
||||||
if (first_option) |opt| {
|
if (first_option) |opt| {
|
||||||
|
|||||||
@@ -26,19 +26,17 @@ const Form = @import("../element/html/Form.zig");
|
|||||||
const Element = @import("../Element.zig");
|
const Element = @import("../Element.zig");
|
||||||
const KeyValueList = @import("../KeyValueList.zig");
|
const KeyValueList = @import("../KeyValueList.zig");
|
||||||
|
|
||||||
const Alloctor = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const FormData = @This();
|
const FormData = @This();
|
||||||
|
|
||||||
_arena: Alloctor,
|
_arena: Allocator,
|
||||||
_list: KeyValueList,
|
_list: KeyValueList,
|
||||||
|
|
||||||
pub fn init(form_: ?*Form, submitter_: ?*Element, page: *Page) !*FormData {
|
pub fn init(form: ?*Form, submitter: ?*Element, page: *Page) !*FormData {
|
||||||
_ = form_;
|
|
||||||
_ = submitter_;
|
|
||||||
return page._factory.create(FormData{
|
return page._factory.create(FormData{
|
||||||
._arena = page.arena,
|
._arena = page.arena,
|
||||||
._list = KeyValueList.init(),
|
._list = try collectForm(page.arena, form, submitter, page),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +106,82 @@ pub const Iterator = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn collectForm(arena: Allocator, form_: ?*Form, submitter_: ?*Element, page: *Page) !KeyValueList {
|
||||||
|
var list: KeyValueList = .empty;
|
||||||
|
const form = form_ orelse return list;
|
||||||
|
|
||||||
|
var elements = try form.getElements(page);
|
||||||
|
var it = try elements.iterator();
|
||||||
|
while (it.next()) |element| {
|
||||||
|
if (element.getAttributeSafe("disabled") != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle image submitters first - they can submit without a name
|
||||||
|
if (element.is(Form.Input)) |input| {
|
||||||
|
if (input._input_type == .image) {
|
||||||
|
const submitter = submitter_ orelse continue;
|
||||||
|
if (submitter != element) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = element.getAttributeSafe("name");
|
||||||
|
const x_key = if (name) |n| try std.fmt.allocPrint(arena, "{s}.x", .{n}) else "x";
|
||||||
|
const y_key = if (name) |n| try std.fmt.allocPrint(arena, "{s}.y", .{n}) else "y";
|
||||||
|
try list.append(arena, x_key, "0");
|
||||||
|
try list.append(arena, y_key, "0");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = element.getAttributeSafe("name") orelse continue;
|
||||||
|
const value = blk: {
|
||||||
|
if (element.is(Form.Input)) |input| {
|
||||||
|
const input_type = input._input_type;
|
||||||
|
if (input_type == .checkbox or input_type == .radio) {
|
||||||
|
if (!input.getChecked()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (input_type == .submit) {
|
||||||
|
const submitter = submitter_ orelse continue;
|
||||||
|
if (submitter != element) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :blk input.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.is(Form.Select)) |select| {
|
||||||
|
if (select.getMultiple() == false) {
|
||||||
|
break :blk select.getValue(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = try select.getSelectedOptions(page);
|
||||||
|
while (options.next()) |option| {
|
||||||
|
try list.append(arena, name, option.as(Form.Select.Option).getValue(page));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element.is(Form.TextArea)) |textarea| {
|
||||||
|
break :blk textarea.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submitter_) |submitter| {
|
||||||
|
if (submitter == element) {
|
||||||
|
// The form iterator only yields form controls. If we're here
|
||||||
|
// all other control types have been handled. So the cast is safe.
|
||||||
|
break :blk element.as(Form.Button).getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
try list.append(arena, name, value);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
pub const bridge = js.Bridge(FormData);
|
pub const bridge = js.Bridge(FormData);
|
||||||
|
|
||||||
@@ -131,97 +205,6 @@ pub const JsApi = struct {
|
|||||||
pub const forEach = bridge.function(FormData.forEach, .{});
|
pub const forEach = bridge.function(FormData.forEach, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
// fn collectForm(form: *Form, submitter_: ?*Element, page: *Page) !KeyValueList {
|
|
||||||
// const arena = page.arena;
|
|
||||||
|
|
||||||
// // Don't use libdom's formGetCollection (aka dom_html_form_element_get_elements)
|
|
||||||
// // It doesn't work with dynamically added elements, because their form
|
|
||||||
// // property doesn't get set. We should fix that.
|
|
||||||
// // However, even once fixed, there are other form-collection features we
|
|
||||||
// // probably want to implement (like disabled fieldsets), so we might want
|
|
||||||
// // to stick with our own walker even if fix libdom to properly support
|
|
||||||
// // dynamically added elements.
|
|
||||||
// const node_list = try @import("../dom/css.zig").querySelectorAll(arena, @ptrCast(@alignCast(form)), "input,select,button,textarea");
|
|
||||||
// const nodes = node_list.nodes.items;
|
|
||||||
|
|
||||||
// var entries: kv.List = .{};
|
|
||||||
// try entries.ensureTotalCapacity(arena, nodes.len);
|
|
||||||
|
|
||||||
// var submitter_included = false;
|
|
||||||
// const submitter_name_ = try getSubmitterName(submitter_);
|
|
||||||
|
|
||||||
// for (nodes) |node| {
|
|
||||||
// const element = parser.nodeToElement(node);
|
|
||||||
|
|
||||||
// // must have a name
|
|
||||||
// const name = try parser.elementGetAttribute(element, "name") orelse continue;
|
|
||||||
// if (try parser.elementGetAttribute(element, "disabled") != null) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const tag = try parser.elementTag(element);
|
|
||||||
// switch (tag) {
|
|
||||||
// .input => {
|
|
||||||
// const tpe = try parser.inputGetType(@ptrCast(element));
|
|
||||||
// if (std.ascii.eqlIgnoreCase(tpe, "image")) {
|
|
||||||
// if (submitter_name_) |submitter_name| {
|
|
||||||
// if (std.mem.eql(u8, submitter_name, name)) {
|
|
||||||
// const key_x = try std.fmt.allocPrint(arena, "{s}.x", .{name});
|
|
||||||
// const key_y = try std.fmt.allocPrint(arena, "{s}.y", .{name});
|
|
||||||
// try entries.appendOwned(arena, key_x, "0");
|
|
||||||
// try entries.appendOwned(arena, key_y, "0");
|
|
||||||
// submitter_included = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (std.ascii.eqlIgnoreCase(tpe, "checkbox") or std.ascii.eqlIgnoreCase(tpe, "radio")) {
|
|
||||||
// if (try parser.inputGetChecked(@ptrCast(element)) == false) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (std.ascii.eqlIgnoreCase(tpe, "submit")) {
|
|
||||||
// if (submitter_name_ == null or !std.mem.eql(u8, submitter_name_.?, name)) {
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// submitter_included = true;
|
|
||||||
// }
|
|
||||||
// const value = try parser.inputGetValue(@ptrCast(element));
|
|
||||||
// try entries.appendOwned(arena, name, value);
|
|
||||||
// },
|
|
||||||
// .select => {
|
|
||||||
// const select: *parser.Select = @ptrCast(node);
|
|
||||||
// try collectSelectValues(arena, select, name, &entries, page);
|
|
||||||
// },
|
|
||||||
// .textarea => {
|
|
||||||
// const textarea: *parser.TextArea = @ptrCast(node);
|
|
||||||
// const value = try parser.textareaGetValue(textarea);
|
|
||||||
// try entries.appendOwned(arena, name, value);
|
|
||||||
// },
|
|
||||||
// .button => if (submitter_name_) |submitter_name| {
|
|
||||||
// if (std.mem.eql(u8, submitter_name, name)) {
|
|
||||||
// const value = (try parser.elementGetAttribute(element, "value")) orelse "";
|
|
||||||
// try entries.appendOwned(arena, name, value);
|
|
||||||
// submitter_included = true;
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// else => unreachable,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (submitter_included == false) {
|
|
||||||
// if (submitter_name_) |submitter_name| {
|
|
||||||
// // this can happen if the submitter is outside the form, but associated
|
|
||||||
// // with the form via a form=ID attribute
|
|
||||||
// const value = (try parser.elementGetAttribute(@ptrCast(submitter_.?), "value")) orelse "";
|
|
||||||
// try entries.appendOwned(arena, submitter_name, value);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return entries;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const testing = @import("../../../testing.zig");
|
const testing = @import("../../../testing.zig");
|
||||||
test "WebApi: FormData" {
|
test "WebApi: FormData" {
|
||||||
try testing.htmlRunner("net/form_data.html", .{});
|
try testing.htmlRunner("net/form_data.html", .{});
|
||||||
|
|||||||
Reference in New Issue
Block a user