mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-14 15:28:57 +00:00
enhance Anchor API
This commit is contained in:
@@ -11,3 +11,62 @@
|
||||
testing.expectEqual('http://127.0.0.1:9582/hello/world/anchor2.html', $('#a2').href)
|
||||
testing.expectEqual('https://www.openmymind.net/Elixirs-With-Statement/', $('#a3').href)
|
||||
</script>
|
||||
|
||||
<a id=link href=foo>OK</a>
|
||||
|
||||
<script id=anchor>
|
||||
let link = $('#link');
|
||||
testing.expectEqual('', link.target);
|
||||
link.target = '_blank';
|
||||
testing.expectEqual('_blank', link.target);
|
||||
link.target = '';
|
||||
|
||||
testing.expectEqual('foo', link.href);
|
||||
link.href = 'https://lightpanda.io/';
|
||||
testing.expectEqual('https://lightpanda.io/', link.href);
|
||||
|
||||
testing.expectEqual('https://lightpanda.io', link.origin);
|
||||
|
||||
link.host = 'lightpanda.io:443';
|
||||
testing.expectEqual('lightpanda.io', link.host);
|
||||
testing.expectEqual('', link.port);
|
||||
testing.expectEqual('lightpanda.io', link.hostname);
|
||||
|
||||
link.host = 'lightpanda.io';
|
||||
testing.expectEqual('lightpanda.io', link.host);
|
||||
testing.expectEqual('', link.port);
|
||||
testing.expectEqual('lightpanda.io', link.hostname);
|
||||
|
||||
testing.expectEqual('lightpanda.io', link.host);
|
||||
testing.expectEqual('lightpanda.io', link.hostname);
|
||||
link.hostname = 'foo.bar';
|
||||
testing.expectEqual('https://foo.bar/', link.href);
|
||||
|
||||
testing.expectEqual('', link.search);
|
||||
link.search = 'q=bar';
|
||||
testing.expectEqual('?q=bar', link.search);
|
||||
testing.expectEqual('https://foo.bar/?q=bar', link.href);
|
||||
|
||||
testing.expectEqual('', link.hash);
|
||||
link.hash = 'frag';
|
||||
testing.expectEqual('#frag', link.hash);
|
||||
testing.expectEqual('https://foo.bar/?q=bar#frag', link.href);
|
||||
|
||||
testing.expectEqual('', link.port);
|
||||
link.port = '443';
|
||||
testing.expectEqual('foo.bar', link.host);
|
||||
testing.expectEqual('foo.bar', link.hostname);
|
||||
testing.expectEqual('https://foo.bar/?q=bar#frag', link.href);
|
||||
link.port = null;
|
||||
testing.expectEqual('https://foo.bar/?q=bar#frag', link.href);
|
||||
|
||||
testing.expectEqual('foo', link.href = 'foo');
|
||||
|
||||
testing.expectEqual('', link.type);
|
||||
link.type = 'text/html';
|
||||
testing.expectEqual('text/html', link.type);
|
||||
|
||||
testing.expectEqual('OK', link.text);
|
||||
link.text = 'foo';
|
||||
testing.expectEqual('foo', link.text);
|
||||
</script>
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
// 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/>.
|
||||
|
||||
const std = @import("std");
|
||||
const js = @import("../../../js/js.zig");
|
||||
const Page = @import("../../../Page.zig");
|
||||
|
||||
const URL = @import("../../URL.zig");
|
||||
const URL = @import("../../../URL.zig");
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
const HtmlElement = @import("../Html.zig");
|
||||
@@ -35,11 +36,194 @@ pub fn asNode(self: *Anchor) *Node {
|
||||
}
|
||||
|
||||
pub fn getHref(self: *Anchor, page: *Page) ![]const u8 {
|
||||
const el = self.asElement();
|
||||
const href = el.getAttributeSafe("href");
|
||||
const element = self.asElement();
|
||||
const href = element.getAttributeSafe("href") orelse "";
|
||||
if (href.len == 0) {
|
||||
return page.url;
|
||||
}
|
||||
|
||||
const first = href[0];
|
||||
if (first == '#' or first == '?' or first == '/' or std.mem.startsWith(u8, href, "../") or std.mem.startsWith(u8, href, "./")) {
|
||||
return URL.resolve(page.call_arena, page.url, href, .{});
|
||||
}
|
||||
|
||||
return href;
|
||||
}
|
||||
|
||||
pub fn setHref(self: *Anchor, value: []const u8, page: *Page) !void {
|
||||
try self.asElement().setAttributeSafe("href", value, page);
|
||||
}
|
||||
|
||||
pub fn getTarget(self: *Anchor) []const u8 {
|
||||
return self.asElement().getAttributeSafe("target") orelse "";
|
||||
}
|
||||
|
||||
pub fn setTarget(self: *Anchor, value: []const u8, page: *Page) !void {
|
||||
try self.asElement().setAttributeSafe("target", value, page);
|
||||
}
|
||||
|
||||
pub fn getOrigin(self: *Anchor, page: *Page) ![]const u8 {
|
||||
const href = try getResolvedHref(self, page);
|
||||
return (try URL.getOrigin(page.call_arena, href)) orelse "null";
|
||||
}
|
||||
|
||||
pub fn getHost(self: *Anchor, page: *Page) ![]const u8 {
|
||||
const href = try getResolvedHref(self, page);
|
||||
return URL.getHost(href);
|
||||
}
|
||||
|
||||
pub fn setHost(self: *Anchor, value: []const u8, page: *Page) !void {
|
||||
const href = try getResolvedHref(self, page);
|
||||
const protocol = URL.getProtocol(href);
|
||||
const pathname = URL.getPathname(href);
|
||||
const search = URL.getSearch(href);
|
||||
const hash = URL.getHash(href);
|
||||
|
||||
// Check if the host includes a port
|
||||
const colon_pos = std.mem.lastIndexOfScalar(u8, value, ':');
|
||||
const clean_host = if (colon_pos) |pos| blk: {
|
||||
const port_str = value[pos + 1 ..];
|
||||
// Remove default ports
|
||||
if (std.mem.eql(u8, protocol, "https:") and std.mem.eql(u8, port_str, "443")) {
|
||||
break :blk value[0..pos];
|
||||
}
|
||||
if (std.mem.eql(u8, protocol, "http:") and std.mem.eql(u8, port_str, "80")) {
|
||||
break :blk value[0..pos];
|
||||
}
|
||||
break :blk value;
|
||||
} else value;
|
||||
|
||||
const new_href = try buildUrl(page.call_arena, protocol, clean_host, pathname, search, hash);
|
||||
try setHref(self, new_href, page);
|
||||
}
|
||||
|
||||
pub fn getHostname(self: *Anchor, page: *Page) ![]const u8 {
|
||||
const href = try getResolvedHref(self, page);
|
||||
return URL.getHostname(href);
|
||||
}
|
||||
|
||||
pub fn setHostname(self: *Anchor, value: []const u8, page: *Page) !void {
|
||||
const href = try getResolvedHref(self, page);
|
||||
const current_port = URL.getPort(href);
|
||||
const new_host = if (current_port.len > 0)
|
||||
try std.fmt.allocPrint(page.call_arena, "{s}:{s}", .{ value, current_port })
|
||||
else
|
||||
value;
|
||||
|
||||
try setHost(self, new_host, page);
|
||||
}
|
||||
|
||||
pub fn getPort(self: *Anchor, page: *Page) ![]const u8 {
|
||||
const href = try getResolvedHref(self, page);
|
||||
return URL.getPort(href);
|
||||
}
|
||||
|
||||
pub fn setPort(self: *Anchor, value: ?[]const u8, page: *Page) !void {
|
||||
const href = try getResolvedHref(self, page);
|
||||
const hostname = URL.getHostname(href);
|
||||
const protocol = URL.getProtocol(href);
|
||||
|
||||
// Handle null or default ports
|
||||
const new_host = if (value) |port_str| blk: {
|
||||
if (port_str.len == 0) {
|
||||
break :blk hostname;
|
||||
}
|
||||
// Check if this is a default port for the protocol
|
||||
if (std.mem.eql(u8, protocol, "https:") and std.mem.eql(u8, port_str, "443")) {
|
||||
break :blk hostname;
|
||||
}
|
||||
if (std.mem.eql(u8, protocol, "http:") and std.mem.eql(u8, port_str, "80")) {
|
||||
break :blk hostname;
|
||||
}
|
||||
break :blk try std.fmt.allocPrint(page.call_arena, "{s}:{s}", .{ hostname, port_str });
|
||||
} else hostname;
|
||||
|
||||
try setHost(self, new_host, page);
|
||||
}
|
||||
|
||||
pub fn getSearch(self: *Anchor, page: *Page) ![]const u8 {
|
||||
const href = try getResolvedHref(self, page);
|
||||
return URL.getSearch(href);
|
||||
}
|
||||
|
||||
pub fn setSearch(self: *Anchor, value: []const u8, page: *Page) !void {
|
||||
const href = try getResolvedHref(self, page);
|
||||
const protocol = URL.getProtocol(href);
|
||||
const host = URL.getHost(href);
|
||||
const pathname = URL.getPathname(href);
|
||||
const hash = URL.getHash(href);
|
||||
|
||||
// Add ? prefix if not present and value is not empty
|
||||
const search = if (value.len > 0 and value[0] != '?')
|
||||
try std.fmt.allocPrint(page.call_arena, "?{s}", .{value})
|
||||
else
|
||||
value;
|
||||
|
||||
const new_href = try buildUrl(page.call_arena, protocol, host, pathname, search, hash);
|
||||
try setHref(self, new_href, page);
|
||||
}
|
||||
|
||||
pub fn getHash(self: *Anchor, page: *Page) ![]const u8 {
|
||||
const href = try getResolvedHref(self, page);
|
||||
return URL.getHash(href);
|
||||
}
|
||||
|
||||
pub fn setHash(self: *Anchor, value: []const u8, page: *Page) !void {
|
||||
const href = try getResolvedHref(self, page);
|
||||
const protocol = URL.getProtocol(href);
|
||||
const host = URL.getHost(href);
|
||||
const pathname = URL.getPathname(href);
|
||||
const search = URL.getSearch(href);
|
||||
|
||||
// Add # prefix if not present and value is not empty
|
||||
const hash = if (value.len > 0 and value[0] != '#')
|
||||
try std.fmt.allocPrint(page.call_arena, "#{s}", .{value})
|
||||
else
|
||||
value;
|
||||
|
||||
const new_href = try buildUrl(page.call_arena, protocol, host, pathname, search, hash);
|
||||
try setHref(self, new_href, page);
|
||||
}
|
||||
|
||||
pub fn getType(self: *Anchor) []const u8 {
|
||||
return self.asElement().getAttributeSafe("type") orelse "";
|
||||
}
|
||||
|
||||
pub fn setType(self: *Anchor, value: []const u8, page: *Page) !void {
|
||||
try self.asElement().setAttributeSafe("type", value, page);
|
||||
}
|
||||
|
||||
pub fn getText(self: *Anchor, page: *Page) ![:0]const u8 {
|
||||
return self.asNode().getTextContentAlloc(page.call_arena);
|
||||
}
|
||||
|
||||
pub fn setText(self: *Anchor, value: []const u8, page: *Page) !void {
|
||||
try self.asNode().setTextContent(value, page);
|
||||
}
|
||||
|
||||
fn getResolvedHref(self: *Anchor, page: *Page) ![:0]const u8 {
|
||||
const href = self.asElement().getAttributeSafe("href");
|
||||
return URL.resolve(page.call_arena, page.url, href orelse "", .{});
|
||||
}
|
||||
|
||||
// Helper function to build a new URL from components
|
||||
fn buildUrl(
|
||||
allocator: std.mem.Allocator,
|
||||
protocol: []const u8,
|
||||
host: []const u8,
|
||||
pathname: []const u8,
|
||||
search: []const u8,
|
||||
hash: []const u8,
|
||||
) ![:0]const u8 {
|
||||
return std.fmt.allocPrintSentinel(allocator, "{s}//{s}{s}{s}{s}", .{
|
||||
protocol,
|
||||
host,
|
||||
pathname,
|
||||
search,
|
||||
hash,
|
||||
}, 0);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Anchor);
|
||||
|
||||
@@ -49,7 +233,16 @@ pub const JsApi = struct {
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const href = bridge.accessor(Anchor.getHref, null, .{});
|
||||
pub const href = bridge.accessor(Anchor.getHref, Anchor.setHref, .{});
|
||||
pub const target = bridge.accessor(Anchor.getTarget, Anchor.setTarget, .{});
|
||||
pub const origin = bridge.accessor(Anchor.getOrigin, null, .{});
|
||||
pub const host = bridge.accessor(Anchor.getHost, Anchor.setHost, .{});
|
||||
pub const hostname = bridge.accessor(Anchor.getHostname, Anchor.setHostname, .{});
|
||||
pub const port = bridge.accessor(Anchor.getPort, Anchor.setPort, .{});
|
||||
pub const search = bridge.accessor(Anchor.getSearch, Anchor.setSearch, .{});
|
||||
pub const hash = bridge.accessor(Anchor.getHash, Anchor.setHash, .{});
|
||||
pub const @"type" = bridge.accessor(Anchor.getType, Anchor.setType, .{});
|
||||
pub const text = bridge.accessor(Anchor.getText, Anchor.setText, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../../testing.zig");
|
||||
|
||||
Reference in New Issue
Block a user