mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
Various input fixes (support for more attributes) based on legacy tests
AbortSignal.timeout function LocalStorage named getter/setter
This commit is contained in:
@@ -23,6 +23,47 @@
|
|||||||
testing.expectEqual('radio', $('#radio1').type)
|
testing.expectEqual('radio', $('#radio1').type)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id=attributes>
|
||||||
|
{
|
||||||
|
const input = $('#text1');
|
||||||
|
|
||||||
|
testing.expectEqual('', input.accept);
|
||||||
|
input.accept = 'anything';
|
||||||
|
testing.expectEqual('anything', input.accept);
|
||||||
|
|
||||||
|
testing.expectEqual('', input.alt);
|
||||||
|
input.alt = 'x1';
|
||||||
|
testing.expectEqual('x1', input.alt);
|
||||||
|
|
||||||
|
testing.expectEqual(false, input.readOnly);
|
||||||
|
input.readOnly = true;
|
||||||
|
testing.expectEqual(true, input.readOnly);
|
||||||
|
input.readOnly = false;
|
||||||
|
testing.expectEqual(false, input.readOnly);
|
||||||
|
|
||||||
|
testing.expectEqual(-1, input.maxLength);
|
||||||
|
input.maxLength = 5;
|
||||||
|
testing.expectEqual(5, input.maxLength);
|
||||||
|
input.maxLength = 'banana';
|
||||||
|
testing.expectEqual(0, input.maxLength);
|
||||||
|
testing.expectError('Error: NegativeValueNotAllowed', () => { input.maxLength = -45;});
|
||||||
|
|
||||||
|
testing.expectEqual(20, input.size);
|
||||||
|
input.size = 5;
|
||||||
|
testing.expectEqual(5, input.size);
|
||||||
|
input.size = -449;
|
||||||
|
testing.expectEqual(20, input.size);
|
||||||
|
testing.expectError('Error: ZeroNotAllowed', () => { input.size = 0; });
|
||||||
|
|
||||||
|
testing.expectEqual('', input.src);
|
||||||
|
input.src = 'foo'
|
||||||
|
testing.expectEqual('http://127.0.0.1:9582/src/browser/tests/element/html/foo', input.src);
|
||||||
|
input.src = '-3'
|
||||||
|
testing.expectEqual('http://127.0.0.1:9582/src/browser/tests/element/html/-3', input.src);
|
||||||
|
input.src = ''
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script id="type_case_insensitive">
|
<script id="type_case_insensitive">
|
||||||
{
|
{
|
||||||
const input = document.createElement('input')
|
const input = document.createElement('input')
|
||||||
@@ -396,3 +437,22 @@
|
|||||||
testing.expectEqual($('#radio3'), document.querySelector('input[type="radio"]:checked'))
|
testing.expectEqual($('#radio3'), document.querySelector('input[type="radio"]:checked'))
|
||||||
testing.expectEqual($('#radio3'), document.querySelector('input[type="radio"][name="group1"]:checked'))
|
testing.expectEqual($('#radio3'), document.querySelector('input[type="radio"][name="group1"]:checked'))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id=related>
|
||||||
|
{
|
||||||
|
let input_checked = document.createElement('input')
|
||||||
|
testing.expectEqual(false, input_checked.defaultChecked);
|
||||||
|
testing.expectEqual(false, input_checked.checked);
|
||||||
|
|
||||||
|
input_checked.defaultChecked = true;
|
||||||
|
testing.expectEqual(true, input_checked.defaultChecked);
|
||||||
|
testing.expectEqual(true, input_checked.checked);
|
||||||
|
|
||||||
|
input_checked.checked = false;
|
||||||
|
testing.expectEqual(true, input_checked.defaultChecked);
|
||||||
|
testing.expectEqual(false, input_checked.checked);
|
||||||
|
|
||||||
|
input_checked.defaultChecked = true;
|
||||||
|
testing.expectEqual(false, input_checked.checked);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
const signal = controller.signal;
|
const signal = controller.signal;
|
||||||
|
|
||||||
testing.expectEqual(false, signal.aborted);
|
testing.expectEqual(false, signal.aborted);
|
||||||
testing.expectEqual(null, signal.reason);
|
testing.expectEqual(undefined, signal.reason);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
const signal = AbortSignal.abort();
|
const signal = AbortSignal.abort();
|
||||||
|
|
||||||
testing.expectEqual(true, signal.aborted);
|
testing.expectEqual(true, signal.aborted);
|
||||||
testing.expectEqual(null, signal.reason);
|
testing.expectEqual("AbortError", signal.reason);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -211,3 +211,42 @@
|
|||||||
testing.expectEqual(2, count); // Still 2, listener was removed
|
testing.expectEqual(2, count); // Still 2, listener was removed
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id=legacy1>
|
||||||
|
var a1 = new AbortController();
|
||||||
|
|
||||||
|
var s1 = a1.signal;
|
||||||
|
testing.expectEqual(undefined, s1.throwIfAborted());
|
||||||
|
testing.expectEqual(undefined, s1.reason);
|
||||||
|
|
||||||
|
let target;;
|
||||||
|
let called = 0;
|
||||||
|
s1.addEventListener('abort', (e) => {
|
||||||
|
called += 1;
|
||||||
|
target = e.target;
|
||||||
|
});
|
||||||
|
|
||||||
|
a1.abort();
|
||||||
|
testing.expectEqual(true, s1.aborted)
|
||||||
|
testing.expectEqual(s1, target)
|
||||||
|
testing.expectEqual('AbortError', s1.reason)
|
||||||
|
testing.expectEqual(1, called)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=abortsignal_abort>
|
||||||
|
var s2 = AbortSignal.abort('over 9000');
|
||||||
|
testing.expectEqual(true, s2.aborted);
|
||||||
|
testing.expectEqual('over 9000', s2.reason);
|
||||||
|
testing.expectEqual('AbortError', AbortSignal.abort().reason);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id=abortsignal_timeout>
|
||||||
|
var s3 = AbortSignal.timeout(10);
|
||||||
|
testing.eventually(() => {
|
||||||
|
testing.expectEqual(true, s3.aborted);
|
||||||
|
testing.expectEqual('TimeoutError', s3.reason);
|
||||||
|
testing.expectError('Error: TimeoutError', () => {
|
||||||
|
s3.throwIfAborted()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
localStorage.setItem('foo', 'bar');
|
localStorage.setItem('foo', 'bar');
|
||||||
testing.expectEqual(1, localStorage.length)
|
testing.expectEqual(1, localStorage.length)
|
||||||
testing.expectEqual('bar', localStorage.getItem('foo'));
|
testing.expectEqual('bar', localStorage.getItem('foo'));
|
||||||
testing.expectEqual('bar', localStorage.key(0));
|
testing.expectEqual('foo', localStorage.key(0));
|
||||||
testing.expectEqual(null, localStorage.key(1));
|
testing.expectEqual(null, localStorage.key(1));
|
||||||
|
|
||||||
localStorage.removeItem('foo');
|
localStorage.removeItem('foo');
|
||||||
|
|||||||
@@ -60,3 +60,31 @@
|
|||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
testing.expectEqual(0, localStorage.length);
|
testing.expectEqual(0, localStorage.length);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id="legacy">
|
||||||
|
localStorage.clear();
|
||||||
|
testing.expectEqual(0, localStorage.length);
|
||||||
|
testing.expectEqual(null, localStorage.getItem('foo'));
|
||||||
|
testing.expectEqual(null, localStorage.key(0));
|
||||||
|
|
||||||
|
localStorage.setItem('foo', 'bar');
|
||||||
|
testing.expectEqual(1, localStorage.length)
|
||||||
|
testing.expectEqual('bar', localStorage.getItem('foo'));
|
||||||
|
testing.expectEqual('foo', localStorage.key(0));
|
||||||
|
testing.expectEqual(null, localStorage.key(1));
|
||||||
|
|
||||||
|
localStorage.removeItem('foo');
|
||||||
|
testing.expectEqual(0, localStorage.length)
|
||||||
|
testing.expectEqual(null, localStorage.getItem('foo'));
|
||||||
|
|
||||||
|
localStorage['foo'] = 'bar';
|
||||||
|
testing.expectEqual(1, localStorage.length);
|
||||||
|
testing.expectEqual('bar', localStorage['foo']);
|
||||||
|
|
||||||
|
localStorage.setItem('a', '1');
|
||||||
|
localStorage.setItem('b', '2');
|
||||||
|
localStorage.setItem('c', '3');
|
||||||
|
testing.expectEqual(4, localStorage.length)
|
||||||
|
localStorage.clear();
|
||||||
|
testing.expectEqual(0, localStorage.length)
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ pub fn getSignal(self: *const AbortController) *AbortSignal {
|
|||||||
return self._signal;
|
return self._signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn abort(self: *AbortController, reason: ?js.Object, page: *Page) !void {
|
pub fn abort(self: *AbortController, reason_: ?js.Object, page: *Page) !void {
|
||||||
try self._signal.abort(reason, page);
|
try self._signal.abort(if (reason_) |r| .{.js_obj = r} else null, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const js = @import("../js/js.zig");
|
const js = @import("../js/js.zig");
|
||||||
|
const log = @import("../../log.zig");
|
||||||
|
|
||||||
const Page = @import("../Page.zig");
|
const Page = @import("../Page.zig");
|
||||||
const Event = @import("Event.zig");
|
const Event = @import("Event.zig");
|
||||||
@@ -27,15 +28,12 @@ const AbortSignal = @This();
|
|||||||
|
|
||||||
_proto: *EventTarget,
|
_proto: *EventTarget,
|
||||||
_aborted: bool = false,
|
_aborted: bool = false,
|
||||||
_reason: ?js.Object = null,
|
_reason: Reason = .undefined,
|
||||||
_on_abort: ?js.Function = null,
|
_on_abort: ?js.Function = null,
|
||||||
|
|
||||||
pub fn init(page: *Page) !*AbortSignal {
|
pub fn init(page: *Page) !*AbortSignal {
|
||||||
return page._factory.eventTarget(AbortSignal{
|
return page._factory.eventTarget(AbortSignal{
|
||||||
._proto = undefined,
|
._proto = undefined,
|
||||||
._aborted = false,
|
|
||||||
._reason = null,
|
|
||||||
._on_abort = null,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +41,7 @@ pub fn getAborted(self: *const AbortSignal) bool {
|
|||||||
return self._aborted;
|
return self._aborted;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getReason(self: *const AbortSignal) ?js.Object {
|
pub fn getReason(self: *const AbortSignal) Reason {
|
||||||
return self._reason;
|
return self._reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,14 +61,22 @@ pub fn asEventTarget(self: *AbortSignal) *EventTarget {
|
|||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn abort(self: *AbortSignal, reason_: ?js.Object, page: *Page) !void {
|
pub fn abort(self: *AbortSignal, reason_: ?Reason, page: *Page) !void {
|
||||||
if (self._aborted) return;
|
if (self._aborted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self._aborted = true;
|
self._aborted = true;
|
||||||
|
|
||||||
// Store the abort reason (default to a simple string if none provided)
|
// Store the abort reason (default to a simple string if none provided)
|
||||||
if (reason_) |reason| {
|
if (reason_) |reason| {
|
||||||
self._reason = try reason.persist();
|
switch (reason) {
|
||||||
|
.js_obj => |js_obj| self._reason = .{.js_obj = try js_obj.persist()},
|
||||||
|
.string => |str| self._reason = .{.string = try page.dupeString(str)},
|
||||||
|
.undefined => self._reason = reason,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self._reason = .{.string = "AbortError"};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch abort event
|
// Dispatch abort event
|
||||||
@@ -86,16 +92,59 @@ pub fn abort(self: *AbortSignal, reason_: ?js.Object, page: *Page) !void {
|
|||||||
// Static method to create an already-aborted signal
|
// Static method to create an already-aborted signal
|
||||||
pub fn createAborted(reason_: ?js.Object, page: *Page) !*AbortSignal {
|
pub fn createAborted(reason_: ?js.Object, page: *Page) !*AbortSignal {
|
||||||
const signal = try init(page);
|
const signal = try init(page);
|
||||||
try signal.abort(reason_, page);
|
try signal.abort(if (reason_) |r| .{.js_obj = r} else null, page);
|
||||||
return signal;
|
return signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn throwIfAborted(self: *const AbortSignal) !void {
|
pub fn createTimeout(delay: u32, page: *Page) !*AbortSignal {
|
||||||
if (self._aborted) {
|
const callback = try page.arena.create(TimeoutCallback);
|
||||||
return error.Aborted;
|
callback.* = .{
|
||||||
}
|
.page = page,
|
||||||
|
.signal = try init(page),
|
||||||
|
};
|
||||||
|
|
||||||
|
try page.scheduler.add(callback, TimeoutCallback.run, delay, .{
|
||||||
|
.name = "AbortSignal.timeout",
|
||||||
|
});
|
||||||
|
|
||||||
|
return callback.signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ThrowIfAborted = union(enum) {
|
||||||
|
exception: js.Exception,
|
||||||
|
undefined: void,
|
||||||
|
};
|
||||||
|
pub fn throwIfAborted(self: *const AbortSignal, page: *Page) !ThrowIfAborted {
|
||||||
|
if (self._aborted) {
|
||||||
|
const exception = switch (self._reason) {
|
||||||
|
.string => |str| page.js.throw(str),
|
||||||
|
.js_obj => |js_obj| page.js.throw(try js_obj.toString()),
|
||||||
|
.undefined => page.js.throw("AbortError"),
|
||||||
|
};
|
||||||
|
return .{ .exception = exception };
|
||||||
|
}
|
||||||
|
return .undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Reason = union(enum) {
|
||||||
|
js_obj: js.Object,
|
||||||
|
string: []const u8,
|
||||||
|
undefined: void,
|
||||||
|
};
|
||||||
|
|
||||||
|
const TimeoutCallback = struct {
|
||||||
|
page: *Page,
|
||||||
|
signal: *AbortSignal,
|
||||||
|
|
||||||
|
fn run(ctx: *anyopaque) !?u32 {
|
||||||
|
const self: *TimeoutCallback = @ptrCast(@alignCast(ctx));
|
||||||
|
self.signal.abort(.{.string = "TimeoutError"}, self.page) catch |err| {
|
||||||
|
log.warn(.app, "abort signal timeout", .{ .err = err });
|
||||||
|
};
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
pub const bridge = js.Bridge(AbortSignal);
|
pub const bridge = js.Bridge(AbortSignal);
|
||||||
|
|
||||||
@@ -116,4 +165,5 @@ pub const JsApi = struct {
|
|||||||
|
|
||||||
// Static method
|
// Static method
|
||||||
pub const abort = bridge.function(AbortSignal.createAborted, .{ .static = true });
|
pub const abort = bridge.function(AbortSignal.createAborted, .{ .static = true });
|
||||||
|
pub const timeout = bridge.function(AbortSignal.createTimeout, .{ .static = true });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ pub fn serializeToString(self: *const XMLSerializer, node: *Node, page: *Page) !
|
|||||||
} else {
|
} else {
|
||||||
try dump.deep(node, .{ .shadow = .skip }, &buf.writer, page);
|
try dump.deep(node, .{ .shadow = .skip }, &buf.writer, page);
|
||||||
}
|
}
|
||||||
return buf.written();
|
// Not sure about this trim. But `dump` is meant to display relatively
|
||||||
|
// pretty HTML, so it does include newlines, which can result in a trailing
|
||||||
|
// newline. XMLSerializer is a bit more strict.
|
||||||
|
return std.mem.trim(u8, buf.written(), &std.ascii.whitespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const JsApi = struct {
|
pub const JsApi = struct {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
const js = @import("../../js/js.zig");
|
const js = @import("../../js/js.zig");
|
||||||
const reflect = @import("../../reflect.zig");
|
const reflect = @import("../../reflect.zig");
|
||||||
|
|
||||||
@@ -61,8 +62,6 @@ pub const Unknown = @import("html/Unknown.zig");
|
|||||||
|
|
||||||
const HtmlElement = @This();
|
const HtmlElement = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
_type: Type,
|
_type: Type,
|
||||||
_proto: *Element,
|
_proto: *Element,
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ _default_value: ?[]const u8 = null,
|
|||||||
_default_checked: bool = false,
|
_default_checked: bool = false,
|
||||||
_value: ?[]const u8 = null,
|
_value: ?[]const u8 = null,
|
||||||
_checked: bool = false,
|
_checked: bool = false,
|
||||||
|
_checked_dirty: bool = false,
|
||||||
_input_type: Type = .text,
|
_input_type: Type = .text,
|
||||||
|
|
||||||
pub fn asElement(self: *Input) *Element {
|
pub fn asElement(self: *Input) *Element {
|
||||||
@@ -122,8 +123,9 @@ pub fn setChecked(self: *Input, checked: bool, page: *Page) !void {
|
|||||||
if (checked and self._input_type == .radio) {
|
if (checked and self._input_type == .radio) {
|
||||||
try self.uncheckRadioGroup(page);
|
try self.uncheckRadioGroup(page);
|
||||||
}
|
}
|
||||||
// This should _not_ call setAttribute. It updates the default state only
|
// This should _not_ call setAttribute. It updates the current state only
|
||||||
self._checked = checked;
|
self._checked = checked;
|
||||||
|
self._checked_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDefaultChecked(self: *const Input) bool {
|
pub fn getDefaultChecked(self: *const Input) bool {
|
||||||
@@ -160,6 +162,78 @@ pub fn setName(self: *Input, name: []const u8, page: *Page) !void {
|
|||||||
try self.asElement().setAttributeSafe("name", name, page);
|
try self.asElement().setAttributeSafe("name", name, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getAccept(self: *const Input) []const u8 {
|
||||||
|
return self.asConstElement().getAttributeSafe("accept") orelse "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setAccept(self: *Input, accept: []const u8, page: *Page) !void {
|
||||||
|
try self.asElement().setAttributeSafe("accept", accept, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAlt(self: *const Input) []const u8 {
|
||||||
|
return self.asConstElement().getAttributeSafe("alt") orelse "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setAlt(self: *Input, alt: []const u8, page: *Page) !void {
|
||||||
|
try self.asElement().setAttributeSafe("alt", alt, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getMaxLength(self: *const Input) i32 {
|
||||||
|
const attr = self.asConstElement().getAttributeSafe("maxlength") orelse return -1;
|
||||||
|
return std.fmt.parseInt(i32, attr, 10) catch -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setMaxLength(self: *Input, max_length: i32, page: *Page) !void {
|
||||||
|
if (max_length < 0) {
|
||||||
|
return error.NegativeValueNotAllowed;
|
||||||
|
}
|
||||||
|
var buf: [32]u8 = undefined;
|
||||||
|
const value = std.fmt.bufPrint(&buf, "{d}", .{max_length}) catch unreachable;
|
||||||
|
try self.asElement().setAttributeSafe("maxlength", value, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSize(self: *const Input) i32 {
|
||||||
|
const attr = self.asConstElement().getAttributeSafe("size") orelse return 20;
|
||||||
|
const parsed = std.fmt.parseInt(i32, attr, 10) catch return 20;
|
||||||
|
return if (parsed == 0) 20 else parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSize(self: *Input, size: i32, page: *Page) !void {
|
||||||
|
if (size == 0) {
|
||||||
|
return error.ZeroNotAllowed;
|
||||||
|
}
|
||||||
|
if (size < 0) {
|
||||||
|
return self.asElement().setAttributeSafe("size", "20", page);
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf: [32]u8 = undefined;
|
||||||
|
const value = std.fmt.bufPrint(&buf, "{d}", .{size}) catch unreachable;
|
||||||
|
try self.asElement().setAttributeSafe("size", value, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getSrc(self: *const Input, page: *Page) ![]const u8 {
|
||||||
|
const src = self.asConstElement().getAttributeSafe("src") orelse return "";
|
||||||
|
// If attribute is explicitly set (even if empty), resolve it against the base URL
|
||||||
|
return @import("../../URL.zig").resolve(page.call_arena, page.base(), src, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setSrc(self: *Input, src: []const u8, page: *Page) !void {
|
||||||
|
const trimmed = std.mem.trim(u8, src, &std.ascii.whitespace);
|
||||||
|
try self.asElement().setAttributeSafe("src", trimmed, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getReadonly(self: *const Input) bool {
|
||||||
|
return self.asConstElement().getAttributeSafe("readonly") != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setReadonly(self: *Input, readonly: bool, page: *Page) !void {
|
||||||
|
if (readonly) {
|
||||||
|
try self.asElement().setAttributeSafe("readonly", "", page);
|
||||||
|
} else {
|
||||||
|
try self.asElement().removeAttribute("readonly", page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getRequired(self: *const Input) bool {
|
pub fn getRequired(self: *const Input) bool {
|
||||||
return self.asConstElement().getAttributeSafe("required") != null;
|
return self.asConstElement().getAttributeSafe("required") != null;
|
||||||
}
|
}
|
||||||
@@ -256,6 +330,12 @@ pub const JsApi = struct {
|
|||||||
pub const disabled = bridge.accessor(Input.getDisabled, Input.setDisabled, .{});
|
pub const disabled = bridge.accessor(Input.getDisabled, Input.setDisabled, .{});
|
||||||
pub const name = bridge.accessor(Input.getName, Input.setName, .{});
|
pub const name = bridge.accessor(Input.getName, Input.setName, .{});
|
||||||
pub const required = bridge.accessor(Input.getRequired, Input.setRequired, .{});
|
pub const required = bridge.accessor(Input.getRequired, Input.setRequired, .{});
|
||||||
|
pub const accept = bridge.accessor(Input.getAccept, Input.setAccept, .{});
|
||||||
|
pub const readOnly = bridge.accessor(Input.getReadonly, Input.setReadonly, .{});
|
||||||
|
pub const alt = bridge.accessor(Input.getAlt, Input.setAlt, .{});
|
||||||
|
pub const maxLength = bridge.accessor(Input.getMaxLength, Input.setMaxLength, .{});
|
||||||
|
pub const size = bridge.accessor(Input.getSize, Input.setSize, .{});
|
||||||
|
pub const src = bridge.accessor(Input.getSrc, Input.setSrc, .{});
|
||||||
pub const form = bridge.accessor(Input.getForm, null, .{});
|
pub const form = bridge.accessor(Input.getForm, null, .{});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -291,11 +371,14 @@ pub const Build = struct {
|
|||||||
.value => self._default_value = value,
|
.value => self._default_value = value,
|
||||||
.checked => {
|
.checked => {
|
||||||
self._default_checked = true;
|
self._default_checked = true;
|
||||||
|
// Only update checked state if it hasn't been manually modified
|
||||||
|
if (!self._checked_dirty) {
|
||||||
self._checked = true;
|
self._checked = true;
|
||||||
// If setting a radio button to checked, uncheck others in the group
|
// If setting a radio button to checked, uncheck others in the group
|
||||||
if (self._input_type == .radio) {
|
if (self._input_type == .radio) {
|
||||||
try self.uncheckRadioGroup(page);
|
try self.uncheckRadioGroup(page);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,7 +391,10 @@ pub const Build = struct {
|
|||||||
.value => self._default_value = null,
|
.value => self._default_value = null,
|
||||||
.checked => {
|
.checked => {
|
||||||
self._default_checked = false;
|
self._default_checked = false;
|
||||||
|
// Only update checked state if it hasn't been manually modified
|
||||||
|
if (!self._checked_dirty) {
|
||||||
self._checked = false;
|
self._checked = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,14 @@ pub fn setType(self: *Script, value: []const u8, page: *Page) !void {
|
|||||||
return self.asElement().setAttributeSafe("type", value, page);
|
return self.asElement().setAttributeSafe("type", value, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getNonce(self: *const Script) []const u8 {
|
||||||
|
return self.asConstElement().getAttributeSafe("nonce") orelse "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setNonce(self: *Script, value: []const u8, page: *Page) !void {
|
||||||
|
return self.asElement().setAttributeSafe("nonce", value, page);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getOnLoad(self: *const Script) ?js.Function {
|
pub fn getOnLoad(self: *const Script) ?js.Function {
|
||||||
return self._on_load;
|
return self._on_load;
|
||||||
}
|
}
|
||||||
@@ -109,6 +117,7 @@ pub const JsApi = struct {
|
|||||||
|
|
||||||
pub const src = bridge.accessor(Script.getSrc, Script.setSrc, .{});
|
pub const src = bridge.accessor(Script.getSrc, Script.setSrc, .{});
|
||||||
pub const @"type" = bridge.accessor(Script.getType, Script.setType, .{});
|
pub const @"type" = bridge.accessor(Script.getType, Script.setType, .{});
|
||||||
|
pub const nonce = bridge.accessor(Script.getNonce, Script.setNonce, .{});
|
||||||
pub const onload = bridge.accessor(Script.getOnLoad, Script.setOnLoad, .{});
|
pub const onload = bridge.accessor(Script.getOnLoad, Script.setOnLoad, .{});
|
||||||
pub const onerror = bridge.accessor(Script.getOnError, Script.setOnError, .{});
|
pub const onerror = bridge.accessor(Script.getOnError, Script.setOnError, .{});
|
||||||
pub const noModule = bridge.accessor(Script.getNoModule, null, .{});
|
pub const noModule = bridge.accessor(Script.getNoModule, null, .{});
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ pub const Lookup = struct {
|
|||||||
pub const removeItem = bridge.function(Lookup.removeItem, .{});
|
pub const removeItem = bridge.function(Lookup.removeItem, .{});
|
||||||
pub const clear = bridge.function(Lookup.clear, .{});
|
pub const clear = bridge.function(Lookup.clear, .{});
|
||||||
pub const key = bridge.function(Lookup.key, .{});
|
pub const key = bridge.function(Lookup.key, .{});
|
||||||
|
pub const @"[str]" = bridge.namedIndexed(Lookup.getItem, Lookup.setItem, null, .{ .null_as_undefined = true });
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,13 @@ pub fn main() !void {
|
|||||||
if (!std.mem.endsWith(u8, entry.basename, ".html")) {
|
if (!std.mem.endsWith(u8, entry.basename, ".html")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (std.mem.indexOf(u8, entry.basename, "navigation") != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (std.mem.indexOf(u8, entry.basename, "history") != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (filter) |f| {
|
if (filter) |f| {
|
||||||
if (std.mem.indexOf(u8, entry.path, f) == null) {
|
if (std.mem.indexOf(u8, entry.path, f) == null) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user