mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-07-30 17:25:58 -04:00
Merge pull request #2917 from lightpanda-io/svg
webapi: Add various SVG types
This commit is contained in:
@@ -335,33 +335,49 @@ pub fn htmlMediaElement(self: *Factory, child: anytype) !*@TypeOf(child) {
|
||||
}
|
||||
|
||||
pub fn svgElement(self: *Factory, tag_name: []const u8, child: anytype) !*@TypeOf(child) {
|
||||
const allocator = self._slab.allocator();
|
||||
const ChildT = @TypeOf(child);
|
||||
|
||||
if (ChildT == Element.Svg) {
|
||||
return self.element(child);
|
||||
}
|
||||
|
||||
const chain = try PrototypeChain(
|
||||
&.{ EventTarget, Node, Element, Element.Svg, ChildT },
|
||||
).allocate(allocator);
|
||||
&.{ EventTarget, Node, Element, Element.Svg, @TypeOf(child) },
|
||||
).allocate(self._slab.allocator());
|
||||
|
||||
try self.setSvgChainBase(chain, tag_name);
|
||||
chain.setLeaf(4, child);
|
||||
return chain.get(4);
|
||||
}
|
||||
|
||||
pub fn svgGraphicsElement(self: *Factory, tag_name: []const u8, child: anytype) !*@TypeOf(child) {
|
||||
const chain = try PrototypeChain(
|
||||
&.{ EventTarget, Node, Element, Element.Svg, Element.Svg.Graphics, @TypeOf(child) },
|
||||
).allocate(self._slab.allocator());
|
||||
|
||||
try self.setSvgChainBase(chain, tag_name);
|
||||
chain.setMiddle(4, Element.Svg.Graphics.Type);
|
||||
chain.setLeaf(5, child);
|
||||
return chain.get(5);
|
||||
}
|
||||
|
||||
pub fn svgGeometryElement(self: *Factory, tag_name: []const u8, child: anytype) !*@TypeOf(child) {
|
||||
const chain = try PrototypeChain(
|
||||
&.{ EventTarget, Node, Element, Element.Svg, Element.Svg.Graphics, Element.Svg.Graphics.Geometry, @TypeOf(child) },
|
||||
).allocate(self._slab.allocator());
|
||||
|
||||
try self.setSvgChainBase(chain, tag_name);
|
||||
chain.setMiddle(4, Element.Svg.Graphics.Type);
|
||||
chain.setMiddle(5, Element.Svg.Graphics.Geometry.Type);
|
||||
chain.setLeaf(6, child);
|
||||
return chain.get(6);
|
||||
}
|
||||
|
||||
fn setSvgChainBase(self: *Factory, chain: anytype, tag_name: []const u8) !void {
|
||||
chain.setRoot(EventTarget.Type);
|
||||
chain.setMiddle(1, Node.Type);
|
||||
chain.setMiddle(2, Element.Type);
|
||||
|
||||
// will never allocate, can't fail
|
||||
const tag_name_str = String.init(self._arena, tag_name, .{}) catch unreachable;
|
||||
|
||||
// Manually set Element.Svg with the tag_name
|
||||
chain.set(3, .{
|
||||
._proto = chain.get(2),
|
||||
._tag_name = tag_name_str,
|
||||
._tag_name = try String.init(self._arena, tag_name, .{}),
|
||||
._type = unionInit(Element.Svg.Type, chain.get(4)),
|
||||
});
|
||||
|
||||
chain.setLeaf(4, child);
|
||||
return chain.get(4);
|
||||
}
|
||||
|
||||
pub fn xhrEventTarget(_: *const Factory, allocator: Allocator, child: anytype) !*@TypeOf(child) {
|
||||
|
||||
@@ -42,6 +42,7 @@ const Event = @import("webapi/Event.zig");
|
||||
const EventTarget = @import("webapi/EventTarget.zig");
|
||||
const Element = @import("webapi/Element.zig");
|
||||
const HtmlElement = @import("webapi/element/Html.zig");
|
||||
const AnimatedString = @import("webapi/svg/AnimatedString.zig");
|
||||
const Window = @import("webapi/Window.zig");
|
||||
const Location = @import("webapi/Location.zig");
|
||||
const Document = @import("webapi/Document.zig");
|
||||
@@ -137,6 +138,7 @@ _element_shadow_roots: Element.ShadowRootLookup = .empty,
|
||||
_node_owner_documents: Node.OwnerDocumentLookup = .empty,
|
||||
_element_scroll_positions: Element.ScrollPositionLookup = .empty,
|
||||
_element_namespace_uris: Element.NamespaceUriLookup = .empty,
|
||||
_svg_animated_strings: AnimatedString.Lookup = .empty,
|
||||
|
||||
// Same as above, but for Nodes (slot assigments apply to both Element AND
|
||||
// Text nodes)
|
||||
|
||||
@@ -879,16 +879,47 @@ pub fn createElementNS(frame: *Frame, namespace: Element.Namespace, name: []cons
|
||||
return createHtmlElementT(frame, Element.Html.Unknown, namespace, attribute_iterator, .{ ._proto = undefined, ._tag_name = tag_name });
|
||||
},
|
||||
.svg => {
|
||||
const tag_name = try String.init(frame.arena, name, .{});
|
||||
if (std.ascii.eqlIgnoreCase(name, "svg")) {
|
||||
return createSvgElementT(frame, Element.Svg, name, attribute_iterator, .{
|
||||
._proto = undefined,
|
||||
._type = .svg,
|
||||
._tag_name = tag_name,
|
||||
});
|
||||
const Graphics = Element.Svg.Graphics;
|
||||
const Geometry = Graphics.Geometry;
|
||||
// SVG tag names are case-sensitive; no lowering before matching.
|
||||
switch (name.len) {
|
||||
1 => switch (name[0]) {
|
||||
'g' => return createSvgGraphicsElementT(frame, Graphics.G, name, attribute_iterator),
|
||||
'a' => return createSvgGraphicsElementT(frame, Graphics.A, name, attribute_iterator),
|
||||
else => {},
|
||||
},
|
||||
3 => switch (@as(u24, @bitCast(name[0..3].*))) {
|
||||
asUint("svg") => return createSvgGraphicsElementT(frame, Graphics.Svg, name, attribute_iterator),
|
||||
asUint("use") => return createSvgGraphicsElementT(frame, Graphics.Use, name, attribute_iterator),
|
||||
else => {},
|
||||
},
|
||||
4 => switch (@as(u32, @bitCast(name[0..4].*))) {
|
||||
asUint("defs") => return createSvgGraphicsElementT(frame, Graphics.Defs, name, attribute_iterator),
|
||||
asUint("rect") => return createSvgGeometryElementT(frame, Geometry.Rect, name, attribute_iterator),
|
||||
asUint("line") => return createSvgGeometryElementT(frame, Geometry.Line, name, attribute_iterator),
|
||||
asUint("path") => return createSvgGeometryElementT(frame, Geometry.Path, name, attribute_iterator),
|
||||
else => {},
|
||||
},
|
||||
5 => switch (@as(u40, @bitCast(name[0..5].*))) {
|
||||
asUint("image") => return createSvgGraphicsElementT(frame, Graphics.Image, name, attribute_iterator),
|
||||
else => {},
|
||||
},
|
||||
6 => switch (@as(u48, @bitCast(name[0..6].*))) {
|
||||
asUint("circle") => return createSvgGeometryElementT(frame, Geometry.Circle, name, attribute_iterator),
|
||||
else => {},
|
||||
},
|
||||
7 => switch (@as(u56, @bitCast(name[0..7].*))) {
|
||||
asUint("ellipse") => return createSvgGeometryElementT(frame, Geometry.Ellipse, name, attribute_iterator),
|
||||
asUint("polygon") => return createSvgGeometryElementT(frame, Geometry.Polygon, name, attribute_iterator),
|
||||
else => {},
|
||||
},
|
||||
8 => switch (@as(u64, @bitCast(name[0..8].*))) {
|
||||
asUint("polyline") => return createSvgGeometryElementT(frame, Geometry.Polyline, name, attribute_iterator),
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
// Other SVG elements (rect, circle, text, g, etc.)
|
||||
const lower = std.ascii.lowerString(&frame.buf, name);
|
||||
const tag = std.meta.stringToEnum(Element.Tag, lower) orelse .unknown;
|
||||
return createSvgElementT(frame, Element.Svg.Generic, name, attribute_iterator, .{ ._proto = undefined, ._tag = tag });
|
||||
@@ -930,7 +961,20 @@ fn createHtmlMediaElementT(frame: *Frame, comptime E: type, namespace: Element.N
|
||||
|
||||
fn createSvgElementT(frame: *Frame, comptime E: type, tag_name: []const u8, attribute_iterator: anytype, svg_element: E) !*Node {
|
||||
const svg_element_ptr = try frame._factory.svgElement(tag_name, svg_element);
|
||||
var element = svg_element_ptr.asElement();
|
||||
return initSvgElement(frame, svg_element_ptr.asElement(), attribute_iterator);
|
||||
}
|
||||
|
||||
fn createSvgGraphicsElementT(frame: *Frame, comptime E: type, tag_name: []const u8, attribute_iterator: anytype) !*Node {
|
||||
const svg_element_ptr = try frame._factory.svgGraphicsElement(tag_name, E{ ._proto = undefined });
|
||||
return initSvgElement(frame, svg_element_ptr.asElement(), attribute_iterator);
|
||||
}
|
||||
|
||||
fn createSvgGeometryElementT(frame: *Frame, comptime E: type, tag_name: []const u8, attribute_iterator: anytype) !*Node {
|
||||
const svg_element_ptr = try frame._factory.svgGeometryElement(tag_name, E{ ._proto = undefined });
|
||||
return initSvgElement(frame, svg_element_ptr.asElement(), attribute_iterator);
|
||||
}
|
||||
|
||||
fn initSvgElement(frame: *Frame, element: *Element, attribute_iterator: anytype) !*Node {
|
||||
element._namespace = .svg;
|
||||
element._attributes.normalize = false;
|
||||
try populateElementAttributes(frame, element, attribute_iterator);
|
||||
|
||||
@@ -995,6 +995,23 @@ pub const PageJsApis = flattenTypes(&.{
|
||||
@import("../webapi/element/html/ValidityState.zig"),
|
||||
@import("../webapi/element/Svg.zig"),
|
||||
@import("../webapi/element/svg/Generic.zig"),
|
||||
@import("../webapi/element/svg/Graphics.zig"),
|
||||
@import("../webapi/element/svg/Geometry.zig"),
|
||||
@import("../webapi/element/svg/Svg.zig"),
|
||||
@import("../webapi/element/svg/G.zig"),
|
||||
@import("../webapi/element/svg/A.zig"),
|
||||
@import("../webapi/element/svg/Use.zig"),
|
||||
@import("../webapi/element/svg/Image.zig"),
|
||||
@import("../webapi/element/svg/Defs.zig"),
|
||||
@import("../webapi/element/svg/Rect.zig"),
|
||||
@import("../webapi/element/svg/Circle.zig"),
|
||||
@import("../webapi/element/svg/Ellipse.zig"),
|
||||
@import("../webapi/element/svg/Line.zig"),
|
||||
@import("../webapi/element/svg/Path.zig"),
|
||||
@import("../webapi/element/svg/Polygon.zig"),
|
||||
@import("../webapi/element/svg/Polyline.zig"),
|
||||
@import("../webapi/svg/AnimatedString.zig"),
|
||||
@import("../webapi/svg/Number.zig"),
|
||||
@import("../webapi/encoding/TextDecoder.zig"),
|
||||
@import("../webapi/encoding/TextEncoder.zig"),
|
||||
@import("../webapi/encoding/TextEncoderStream.zig"),
|
||||
|
||||
73
src/browser/tests/element/svg/animated_string.html
Normal file
73
src/browser/tests/element/svg/animated_string.html
Normal file
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<svg id=root class="chart wide">
|
||||
<use id=use1 xlink:href="#icon"></use>
|
||||
<use id=use2 href="#modern" xlink:href="#legacy"></use>
|
||||
<a id=link1></a>
|
||||
</svg>
|
||||
<div id=plain class="html-class"></div>
|
||||
|
||||
<script id=className>
|
||||
{
|
||||
const root = $('#root');
|
||||
testing.expectEqual('object', typeof root.className);
|
||||
testing.expectEqual(true, root.className instanceof SVGAnimatedString);
|
||||
testing.expectEqual('chart wide', root.className.baseVal);
|
||||
testing.expectEqual('chart wide', root.className.animVal);
|
||||
|
||||
// [SameObject]
|
||||
testing.expectEqual(true, root.className === root.className);
|
||||
|
||||
root.className.baseVal = 'chart narrow';
|
||||
testing.expectEqual('chart narrow', root.getAttribute('class'));
|
||||
testing.expectEqual('chart narrow', root.className.animVal);
|
||||
testing.expectEqual(true, root.classList.contains('narrow'));
|
||||
|
||||
root.setAttribute('class', 'other');
|
||||
testing.expectEqual('other', root.className.baseVal);
|
||||
|
||||
// The SVG className accessor doesn't leak onto HTML elements.
|
||||
testing.expectEqual('html-class', $('#plain').className);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=href>
|
||||
{
|
||||
// The parser stores xlink:href under its local name, href.
|
||||
const use1 = $('#use1');
|
||||
testing.expectEqual(true, use1.href instanceof SVGAnimatedString);
|
||||
testing.expectEqual('#icon', use1.href.baseVal);
|
||||
testing.expectEqual('#icon', use1.href.animVal);
|
||||
testing.expectEqual(true, use1.href === use1.href);
|
||||
testing.expectEqual('#modern', $('#use2').href.baseVal);
|
||||
|
||||
// Writes land in xlink:href in browsers (it's the attribute that's set)
|
||||
// but in href for us since the parser stored it there; assert the common
|
||||
// observable.
|
||||
use1.href.baseVal = '#other';
|
||||
testing.expectEqual('#other', use1.href.baseVal);
|
||||
|
||||
// setAttributeNS stores under the local name, so a runtime xlink:href
|
||||
// lands in href and reflects like the parsed variant. (Browsers keep the
|
||||
// xlink:href attribute but reflect it the same way.)
|
||||
const use3 = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
||||
use3.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#runtime');
|
||||
testing.expectEqual('#runtime', use3.href.baseVal);
|
||||
|
||||
// A namespace-less setAttribute('xlink:href') is a literal attribute in no
|
||||
// namespace: not reflected, in browsers or here.
|
||||
const use4 = document.createElementNS('http://www.w3.org/2000/svg', 'use');
|
||||
use4.setAttribute('xlink:href', '#literal');
|
||||
testing.expectEqual('', use4.href.baseVal);
|
||||
use4.href.baseVal = '#written';
|
||||
testing.expectEqual('#written', use4.getAttribute('href'));
|
||||
testing.expectEqual(true, use4.hasAttribute('xlink:href'));
|
||||
|
||||
// No href attribute at all: set writes to href.
|
||||
const link = $('#link1');
|
||||
testing.expectEqual('', link.href.baseVal);
|
||||
link.href.baseVal = '/docs';
|
||||
testing.expectEqual('/docs', link.getAttribute('href'));
|
||||
}
|
||||
</script>
|
||||
115
src/browser/tests/element/svg/hierarchy.html
Normal file
115
src/browser/tests/element/svg/hierarchy.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<svg id=root width="200" height="100">
|
||||
<g id=group>
|
||||
<rect id=rect1 width="10" height="10"></rect>
|
||||
<circle id=circle1 r="5"></circle>
|
||||
<ellipse id=ellipse1></ellipse>
|
||||
<line id=line1></line>
|
||||
<polygon id=polygon1></polygon>
|
||||
<polyline id=polyline1></polyline>
|
||||
</g>
|
||||
<path id=path1 d="M 0 0 L 10 10"></path>
|
||||
<a id=link1></a>
|
||||
<use id=use1></use>
|
||||
<image id=image1></image>
|
||||
<defs id=defs1></defs>
|
||||
<filter id=filter1></filter>
|
||||
</svg>
|
||||
|
||||
<script id=classes>
|
||||
{
|
||||
testing.expectEqual('function', typeof SVGElement);
|
||||
testing.expectEqual('function', typeof SVGGraphicsElement);
|
||||
testing.expectEqual('function', typeof SVGGeometryElement);
|
||||
testing.expectEqual('function', typeof SVGSVGElement);
|
||||
testing.expectEqual('function', typeof SVGGElement);
|
||||
testing.expectEqual('function', typeof SVGAElement);
|
||||
testing.expectEqual('function', typeof SVGUseElement);
|
||||
testing.expectEqual('function', typeof SVGImageElement);
|
||||
testing.expectEqual('function', typeof SVGDefsElement);
|
||||
testing.expectEqual('function', typeof SVGRectElement);
|
||||
testing.expectEqual('function', typeof SVGCircleElement);
|
||||
testing.expectEqual('function', typeof SVGEllipseElement);
|
||||
testing.expectEqual('function', typeof SVGLineElement);
|
||||
testing.expectEqual('function', typeof SVGPathElement);
|
||||
testing.expectEqual('function', typeof SVGPolygonElement);
|
||||
testing.expectEqual('function', typeof SVGPolylineElement);
|
||||
testing.expectEqual('function', typeof SVGAnimatedString);
|
||||
testing.expectEqual('function', typeof SVGNumber);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=instanceof>
|
||||
{
|
||||
const root = $('#root');
|
||||
testing.expectEqual(true, root instanceof SVGSVGElement);
|
||||
testing.expectEqual(true, root instanceof SVGGraphicsElement);
|
||||
testing.expectEqual(true, root instanceof SVGElement);
|
||||
testing.expectEqual(true, root instanceof Element);
|
||||
testing.expectEqual(false, root instanceof SVGGeometryElement);
|
||||
testing.expectEqual('svg', root.tagName);
|
||||
|
||||
const rect = $('#rect1');
|
||||
testing.expectEqual(true, rect instanceof SVGRectElement);
|
||||
testing.expectEqual(true, rect instanceof SVGGeometryElement);
|
||||
testing.expectEqual(true, rect instanceof SVGGraphicsElement);
|
||||
testing.expectEqual(true, rect instanceof SVGElement);
|
||||
testing.expectEqual(false, rect instanceof SVGSVGElement);
|
||||
testing.expectEqual('rect', rect.tagName);
|
||||
testing.expectEqual('10', rect.getAttribute('width'));
|
||||
|
||||
testing.expectEqual(true, $('#group') instanceof SVGGElement);
|
||||
testing.expectEqual(true, $('#circle1') instanceof SVGCircleElement);
|
||||
testing.expectEqual(true, $('#ellipse1') instanceof SVGEllipseElement);
|
||||
testing.expectEqual(true, $('#line1') instanceof SVGLineElement);
|
||||
testing.expectEqual(true, $('#polygon1') instanceof SVGPolygonElement);
|
||||
testing.expectEqual(true, $('#polyline1') instanceof SVGPolylineElement);
|
||||
testing.expectEqual(true, $('#path1') instanceof SVGPathElement);
|
||||
testing.expectEqual(true, $('#link1') instanceof SVGAElement);
|
||||
testing.expectEqual(true, $('#use1') instanceof SVGUseElement);
|
||||
testing.expectEqual(true, $('#image1') instanceof SVGImageElement);
|
||||
testing.expectEqual(true, $('#defs1') instanceof SVGDefsElement);
|
||||
|
||||
// Elements without a dedicated type stay generic, but are still SVGElement.
|
||||
const filter = $('#filter1');
|
||||
testing.expectEqual(true, filter instanceof SVGElement);
|
||||
testing.expectEqual(false, filter instanceof SVGGraphicsElement);
|
||||
testing.expectEqual('filter', filter.tagName);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createElementNS>
|
||||
{
|
||||
const NS = 'http://www.w3.org/2000/svg';
|
||||
const rect = document.createElementNS(NS, 'rect');
|
||||
testing.expectEqual(true, rect instanceof SVGRectElement);
|
||||
testing.expectEqual('rect', rect.tagName);
|
||||
|
||||
// SVG tag names are case-sensitive: 'RECT' isn't a rect element.
|
||||
const notRect = document.createElementNS(NS, 'RECT');
|
||||
testing.expectEqual(false, notRect instanceof SVGRectElement);
|
||||
testing.expectEqual(true, notRect instanceof SVGElement);
|
||||
testing.expectEqual('RECT', notRect.tagName);
|
||||
|
||||
const svg = document.createElementNS(NS, 'svg');
|
||||
testing.expectEqual(true, svg instanceof SVGSVGElement);
|
||||
|
||||
// querySelector still matches both dedicated and generic types.
|
||||
testing.expectEqual($('#rect1'), document.querySelector('#root rect'));
|
||||
testing.expectEqual($('#use1'), document.querySelector('#root use'));
|
||||
testing.expectEqual($('#filter1'), document.querySelector('#root filter'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=hasAttributeNS>
|
||||
{
|
||||
// Parser-created attributes have a null namespace. We ignore attribute
|
||||
// namespaces, so only null/'' lookups behave like a real browser here.
|
||||
const rect = $('#rect1');
|
||||
testing.expectEqual(true, rect.hasAttributeNS(null, 'width'));
|
||||
testing.expectEqual(true, rect.hasAttributeNS('', 'width'));
|
||||
testing.expectEqual(false, rect.hasAttributeNS(null, 'nope'));
|
||||
}
|
||||
</script>
|
||||
85
src/browser/tests/element/svg/svgsvg.html
Normal file
85
src/browser/tests/element/svg/svgsvg.html
Normal file
@@ -0,0 +1,85 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<svg id=first>
|
||||
<g>
|
||||
<rect id=inner-rect></rect>
|
||||
<svg id=nested>
|
||||
<circle id=nested-circle></circle>
|
||||
</svg>
|
||||
</g>
|
||||
</svg>
|
||||
<svg id=second>
|
||||
<rect id=other-rect></rect>
|
||||
</svg>
|
||||
|
||||
<script id=getElementById>
|
||||
{
|
||||
const first = $('#first');
|
||||
testing.expectEqual($('#inner-rect'), first.getElementById('inner-rect'));
|
||||
testing.expectEqual($('#nested-circle'), first.getElementById('nested-circle'));
|
||||
|
||||
// Scoped to the subtree, unlike document.getElementById.
|
||||
testing.expectEqual(null, first.getElementById('other-rect'));
|
||||
testing.expectEqual(null, $('#second').getElementById('inner-rect'));
|
||||
testing.expectEqual(null, first.getElementById(''));
|
||||
testing.expectEqual(null, first.getElementById('missing'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=factories>
|
||||
{
|
||||
// SVG2 defines SVGPoint/SVGMatrix/SVGRect as aliases of the DOM geometry
|
||||
// types, which is what we return. Browsers (Chrome and Firefox both) still
|
||||
// ship distinct legacy interfaces, so the instanceof and DOM-geometry-only
|
||||
// members (w, is2D) fail there. The DOM types are functional supersets of
|
||||
// the legacy ones, so site-facing behavior only gains members.
|
||||
const svg = $('#first');
|
||||
|
||||
const point = svg.createSVGPoint();
|
||||
testing.expectEqual(true, point instanceof DOMPoint);
|
||||
testing.expectEqual(0, point.x);
|
||||
testing.expectEqual(0, point.y);
|
||||
testing.expectEqual(1, point.w);
|
||||
point.x = 5;
|
||||
testing.expectEqual(5, point.x);
|
||||
|
||||
const matrix = svg.createSVGMatrix();
|
||||
testing.expectEqual(true, matrix instanceof DOMMatrix);
|
||||
testing.expectEqual(1, matrix.a);
|
||||
testing.expectEqual(0, matrix.b);
|
||||
testing.expectEqual(1, matrix.d);
|
||||
testing.expectEqual(true, matrix.is2D);
|
||||
|
||||
const rect = svg.createSVGRect();
|
||||
testing.expectEqual(true, rect instanceof DOMRect);
|
||||
testing.expectEqual(0, rect.x);
|
||||
testing.expectEqual(0, rect.width);
|
||||
|
||||
const number = svg.createSVGNumber();
|
||||
testing.expectEqual(true, number instanceof SVGNumber);
|
||||
testing.expectEqual(0, number.value);
|
||||
number.value = 4.5;
|
||||
testing.expectEqual(4.5, number.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=owner>
|
||||
{
|
||||
const first = $('#first');
|
||||
const nested = $('#nested');
|
||||
|
||||
testing.expectEqual(null, first.ownerSVGElement);
|
||||
testing.expectEqual(null, first.viewportElement);
|
||||
|
||||
testing.expectEqual(first, $('#inner-rect').ownerSVGElement);
|
||||
testing.expectEqual(first, $('#inner-rect').viewportElement);
|
||||
testing.expectEqual(first, nested.ownerSVGElement);
|
||||
testing.expectEqual(nested, $('#nested-circle').ownerSVGElement);
|
||||
testing.expectEqual($('#second'), $('#other-rect').ownerSVGElement);
|
||||
|
||||
// A foreignObject starts a new SVG document fragment.
|
||||
first.innerHTML += '<foreignObject><div><svg id=foreign-svg></svg></div></foreignObject>';
|
||||
testing.expectEqual(null, $('#foreign-svg').ownerSVGElement);
|
||||
}
|
||||
</script>
|
||||
@@ -126,7 +126,7 @@ pub fn is(self: *Element, comptime T: type) ?*T {
|
||||
if (T == Svg) {
|
||||
return svg;
|
||||
}
|
||||
if (comptime std.mem.startsWith(u8, type_name, "webapi.element.svg.")) {
|
||||
if (comptime std.mem.startsWith(u8, type_name, "browser.webapi.element.svg.")) {
|
||||
return svg.is(T);
|
||||
}
|
||||
},
|
||||
@@ -583,6 +583,22 @@ pub fn hasAttribute(self: *const Element, name: String, frame: *Frame) !bool {
|
||||
return value != null;
|
||||
}
|
||||
|
||||
/// Like getAttributeNS, the namespace is currently ignored.
|
||||
pub fn hasAttributeNS(
|
||||
self: *const Element,
|
||||
maybe_namespace: ?[]const u8,
|
||||
local_name: String,
|
||||
frame: *Frame,
|
||||
) !bool {
|
||||
if (maybe_namespace) |namespace| {
|
||||
if (!std.mem.eql(u8, namespace, "http://www.w3.org/1999/xhtml")) {
|
||||
log.warn(.not_implemented, "Element.hasAttributeNS", .{ .namespace = namespace });
|
||||
}
|
||||
}
|
||||
|
||||
return self.hasAttribute(local_name, frame);
|
||||
}
|
||||
|
||||
pub fn hasAttributeSafe(self: *const Element, name: String) bool {
|
||||
return self._attributes.hasSafe(name);
|
||||
}
|
||||
@@ -1673,10 +1689,7 @@ pub fn getTag(self: *const Element) Tag {
|
||||
.head => .head,
|
||||
.unknown => .unknown,
|
||||
},
|
||||
.svg => |se| switch (se._type) {
|
||||
.svg => .svg,
|
||||
.generic => |g| g._tag,
|
||||
},
|
||||
.svg => |se| se.getTag(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1940,6 +1953,7 @@ pub const JsApi = struct {
|
||||
pub const style = bridge.accessor(Element.getOrCreateStyle, Element.setStyle, .{});
|
||||
pub const attributes = bridge.accessor(Element.getAttributeNamedNodeMap, null, .{});
|
||||
pub const hasAttribute = bridge.function(Element.hasAttribute, .{});
|
||||
pub const hasAttributeNS = bridge.function(Element.hasAttributeNS, .{});
|
||||
pub const hasAttributes = bridge.function(Element.hasAttributes, .{});
|
||||
pub const getAttribute = bridge.function(Element.getAttribute, .{});
|
||||
pub const getAttributeNS = bridge.function(Element.getAttributeNS, .{});
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
// 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 lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
|
||||
const Node = @import("../Node.zig");
|
||||
const Element = @import("../Element.zig");
|
||||
const AnimatedString = @import("../svg/AnimatedString.zig");
|
||||
pub const Generic = @import("svg/Generic.zig");
|
||||
pub const Graphics = @import("svg/Graphics.zig");
|
||||
|
||||
const String = lp.String;
|
||||
|
||||
@@ -32,24 +36,49 @@ _proto: *Element,
|
||||
_tag_name: String, // Svg elements are case-preserving
|
||||
|
||||
pub const Type = union(enum) {
|
||||
svg,
|
||||
graphics: *Graphics,
|
||||
generic: *Generic,
|
||||
};
|
||||
|
||||
pub fn is(self: *Svg, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |f| {
|
||||
if (@field(Type, f.name) == self._type) {
|
||||
if (f.type == T) {
|
||||
return &@field(self._type, f.name);
|
||||
switch (self._type) {
|
||||
.graphics => |g| {
|
||||
if (T == Graphics) {
|
||||
return g;
|
||||
}
|
||||
if (f.type == *T) {
|
||||
return @field(self._type, f.name);
|
||||
return g.is(T);
|
||||
},
|
||||
.generic => |g| {
|
||||
if (T == Generic) {
|
||||
return g;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getTag(self: *const Svg) Element.Tag {
|
||||
return switch (self._type) {
|
||||
.graphics => |g| switch (g._type) {
|
||||
.svg => .svg,
|
||||
.g => .g,
|
||||
// No dedicated Element.Tag values; tag-name matching falls back
|
||||
// to _tag_name, like it does for generic SVG elements.
|
||||
.a, .use, .image, .defs => .unknown,
|
||||
.geometry => |geo| switch (geo._type) {
|
||||
.rect => .rect,
|
||||
.circle => .circle,
|
||||
.ellipse => .ellipse,
|
||||
.line => .line,
|
||||
.path => .path,
|
||||
.polygon => .polygon,
|
||||
.polyline => .polyline,
|
||||
},
|
||||
},
|
||||
.generic => |g| g._tag,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn asElement(self: *Svg) *Element {
|
||||
return self._proto;
|
||||
}
|
||||
@@ -57,6 +86,25 @@ pub fn asNode(self: *Svg) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
// The nearest ancestor <svg> element, null when this is the outermost svg.
|
||||
pub fn getOwnerSvgElement(self: *Svg) ?*Graphics.Svg {
|
||||
var node = self.asNode().parentNode();
|
||||
while (node) |n| : (node = n.parentNode()) {
|
||||
const element = n.is(Element) orelse return null;
|
||||
if (element._namespace != .svg) {
|
||||
return null;
|
||||
}
|
||||
const svg = element.as(Svg);
|
||||
if (svg.is(Graphics.Svg)) |gfx_svg| {
|
||||
return gfx_svg;
|
||||
}
|
||||
if (svg._tag_name.eql(.wrap("foreignObject"))) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Svg);
|
||||
|
||||
@@ -65,6 +113,16 @@ pub const JsApi = struct {
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
// Overrides el.className to return a readonly AnimatedString
|
||||
pub const className = bridge.accessor(_className, null, .{});
|
||||
fn _className(self: *Svg, frame: *Frame) !*AnimatedString {
|
||||
return AnimatedString.getOrCreate(self.asElement(), .class, frame);
|
||||
}
|
||||
|
||||
pub const ownerSVGElement = bridge.accessor(Svg.getOwnerSvgElement, null, .{});
|
||||
// closest thing we can provide
|
||||
pub const viewportElement = bridge.accessor(Svg.getOwnerSvgElement, null, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
|
||||
51
src/browser/webapi/element/svg/A.zig
Normal file
51
src/browser/webapi/element/svg/A.zig
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
const Frame = @import("../../../Frame.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
const AnimatedString = @import("../../svg/AnimatedString.zig");
|
||||
|
||||
const Graphics = @import("Graphics.zig");
|
||||
|
||||
const A = @This();
|
||||
_proto: *Graphics,
|
||||
|
||||
pub fn asElement(self: *A) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *A) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(A);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGAElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const href = bridge.accessor(_href, null, .{});
|
||||
fn _href(self: *A, frame: *Frame) !*AnimatedString {
|
||||
return AnimatedString.getOrCreate(self.asElement(), .href, frame);
|
||||
}
|
||||
};
|
||||
44
src/browser/webapi/element/svg/Circle.zig
Normal file
44
src/browser/webapi/element/svg/Circle.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Geometry = @import("Geometry.zig");
|
||||
|
||||
const Circle = @This();
|
||||
_proto: *Geometry,
|
||||
|
||||
pub fn asElement(self: *Circle) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Circle) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Circle);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGCircleElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
44
src/browser/webapi/element/svg/Defs.zig
Normal file
44
src/browser/webapi/element/svg/Defs.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Graphics = @import("Graphics.zig");
|
||||
|
||||
const Defs = @This();
|
||||
_proto: *Graphics,
|
||||
|
||||
pub fn asElement(self: *Defs) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Defs) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Defs);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGDefsElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
44
src/browser/webapi/element/svg/Ellipse.zig
Normal file
44
src/browser/webapi/element/svg/Ellipse.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Geometry = @import("Geometry.zig");
|
||||
|
||||
const Ellipse = @This();
|
||||
_proto: *Geometry,
|
||||
|
||||
pub fn asElement(self: *Ellipse) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Ellipse) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Ellipse);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGEllipseElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
44
src/browser/webapi/element/svg/G.zig
Normal file
44
src/browser/webapi/element/svg/G.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Graphics = @import("Graphics.zig");
|
||||
|
||||
const G = @This();
|
||||
_proto: *Graphics,
|
||||
|
||||
pub fn asElement(self: *G) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *G) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(G);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGGElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
@@ -17,8 +17,10 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Svg = @import("../Svg.zig");
|
||||
|
||||
const Generic = @This();
|
||||
|
||||
73
src/browser/webapi/element/svg/Geometry.zig
Normal file
73
src/browser/webapi/element/svg/Geometry.zig
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Graphics = @import("Graphics.zig");
|
||||
pub const Rect = @import("Rect.zig");
|
||||
pub const Circle = @import("Circle.zig");
|
||||
pub const Ellipse = @import("Ellipse.zig");
|
||||
pub const Line = @import("Line.zig");
|
||||
pub const Path = @import("Path.zig");
|
||||
pub const Polygon = @import("Polygon.zig");
|
||||
pub const Polyline = @import("Polyline.zig");
|
||||
|
||||
const Geometry = @This();
|
||||
_proto: *Graphics,
|
||||
_type: Type,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
rect: *Rect,
|
||||
circle: *Circle,
|
||||
ellipse: *Ellipse,
|
||||
line: *Line,
|
||||
path: *Path,
|
||||
polygon: *Polygon,
|
||||
polyline: *Polyline,
|
||||
};
|
||||
|
||||
pub fn is(self: *Geometry, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |f| {
|
||||
if (@field(Type, f.name) == self._type) {
|
||||
if (f.type == *T) {
|
||||
return @field(self._type, f.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn asElement(self: *Geometry) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Geometry) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Geometry);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGGeometryElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
75
src/browser/webapi/element/svg/Graphics.zig
Normal file
75
src/browser/webapi/element/svg/Graphics.zig
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
const SvgElement = @import("../Svg.zig");
|
||||
|
||||
pub const Svg = @import("Svg.zig");
|
||||
pub const G = @import("G.zig");
|
||||
pub const A = @import("A.zig");
|
||||
pub const Use = @import("Use.zig");
|
||||
pub const Image = @import("Image.zig");
|
||||
pub const Defs = @import("Defs.zig");
|
||||
pub const Geometry = @import("Geometry.zig");
|
||||
|
||||
const Graphics = @This();
|
||||
_proto: *SvgElement,
|
||||
_type: Type,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
svg: *Svg,
|
||||
g: *G,
|
||||
a: *A,
|
||||
use: *Use,
|
||||
image: *Image,
|
||||
defs: *Defs,
|
||||
geometry: *Geometry,
|
||||
};
|
||||
|
||||
pub fn is(self: *Graphics, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |f| {
|
||||
if (@field(Type, f.name) == self._type) {
|
||||
if (f.type == *T) {
|
||||
return @field(self._type, f.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (self._type == .geometry) {
|
||||
return self._type.geometry.is(T);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn asElement(self: *Graphics) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Graphics) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Graphics);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGGraphicsElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
51
src/browser/webapi/element/svg/Image.zig
Normal file
51
src/browser/webapi/element/svg/Image.zig
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
const Frame = @import("../../../Frame.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
const AnimatedString = @import("../../svg/AnimatedString.zig");
|
||||
|
||||
const Graphics = @import("Graphics.zig");
|
||||
|
||||
const Image = @This();
|
||||
_proto: *Graphics,
|
||||
|
||||
pub fn asElement(self: *Image) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Image) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Image);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGImageElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const href = bridge.accessor(_href, null, .{});
|
||||
fn _href(self: *Image, frame: *Frame) !*AnimatedString {
|
||||
return AnimatedString.getOrCreate(self.asElement(), .href, frame);
|
||||
}
|
||||
};
|
||||
44
src/browser/webapi/element/svg/Line.zig
Normal file
44
src/browser/webapi/element/svg/Line.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Geometry = @import("Geometry.zig");
|
||||
|
||||
const Line = @This();
|
||||
_proto: *Geometry,
|
||||
|
||||
pub fn asElement(self: *Line) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Line) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Line);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGLineElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
44
src/browser/webapi/element/svg/Path.zig
Normal file
44
src/browser/webapi/element/svg/Path.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Geometry = @import("Geometry.zig");
|
||||
|
||||
const Path = @This();
|
||||
_proto: *Geometry,
|
||||
|
||||
pub fn asElement(self: *Path) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Path) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Path);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGPathElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
44
src/browser/webapi/element/svg/Polygon.zig
Normal file
44
src/browser/webapi/element/svg/Polygon.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Geometry = @import("Geometry.zig");
|
||||
|
||||
const Polygon = @This();
|
||||
_proto: *Geometry,
|
||||
|
||||
pub fn asElement(self: *Polygon) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Polygon) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Polygon);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGPolygonElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
44
src/browser/webapi/element/svg/Polyline.zig
Normal file
44
src/browser/webapi/element/svg/Polyline.zig
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
|
||||
const Geometry = @import("Geometry.zig");
|
||||
|
||||
const Polyline = @This();
|
||||
_proto: *Geometry,
|
||||
|
||||
pub fn asElement(self: *Polyline) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Polyline) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Polyline);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGPolylineElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
@@ -19,13 +19,13 @@
|
||||
const js = @import("../../../js/js.zig");
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
const Svg = @import("../Svg.zig");
|
||||
const Geometry = @import("Geometry.zig");
|
||||
|
||||
const Rect = @This();
|
||||
_proto: *Svg,
|
||||
_proto: *Geometry,
|
||||
|
||||
pub fn asElement(self: *Rect) *Element {
|
||||
return self._proto._proto;
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Rect) *Node {
|
||||
return self.asElement().asNode();
|
||||
|
||||
95
src/browser/webapi/element/svg/Svg.zig
Normal file
95
src/browser/webapi/element/svg/Svg.zig
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 Frame = @import("../../../Frame.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
const TreeWalker = @import("../../TreeWalker.zig");
|
||||
const DOMRect = @import("../../DOMRect.zig");
|
||||
const DOMPoint = @import("../../DOMPoint.zig");
|
||||
const DOMMatrix = @import("../../DOMMatrix.zig");
|
||||
const SvgNumber = @import("../../svg/Number.zig");
|
||||
|
||||
const Graphics = @import("Graphics.zig");
|
||||
|
||||
const Svg = @This();
|
||||
_proto: *Graphics,
|
||||
|
||||
pub fn asElement(self: *Svg) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Svg) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub fn getElementById(self: *Svg, id: []const u8) ?*Element {
|
||||
if (id.len == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var tw = TreeWalker.Full.Elements.init(self.asNode(), .{});
|
||||
while (tw.next()) |el| {
|
||||
const element_id = el.getAttributeSafe(comptime .wrap("id")) orelse continue;
|
||||
if (std.mem.eql(u8, element_id, id)) {
|
||||
return el;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Svg);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGSVGElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const getElementById = bridge.function(Svg.getElementById, .{});
|
||||
|
||||
pub const createSVGPoint = bridge.function(_createSVGPoint, .{});
|
||||
fn _createSVGPoint(_: *Svg, frame: *Frame) !*DOMPoint {
|
||||
return DOMPoint.create(0, 0, 0, 1, frame._page);
|
||||
}
|
||||
|
||||
pub const createSVGMatrix = bridge.function(_createSVGMatrix, .{});
|
||||
fn _createSVGMatrix(_: *Svg, frame: *Frame) !*DOMMatrix {
|
||||
const identity: [16]f64 = .{
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
};
|
||||
return DOMMatrix.create(identity, true, frame._page);
|
||||
}
|
||||
|
||||
pub const createSVGRect = bridge.function(_createSVGRect, .{});
|
||||
fn _createSVGRect(_: *Svg, frame: *Frame) !*DOMRect {
|
||||
return DOMRect.init(0, 0, 0, 0, frame);
|
||||
}
|
||||
|
||||
pub const createSVGNumber = bridge.function(_createSVGNumber, .{});
|
||||
fn _createSVGNumber(_: *Svg, frame: *Frame) !*SvgNumber {
|
||||
return frame._factory.create(SvgNumber{});
|
||||
}
|
||||
};
|
||||
51
src/browser/webapi/element/svg/Use.zig
Normal file
51
src/browser/webapi/element/svg/Use.zig
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../../js/js.zig");
|
||||
const Frame = @import("../../../Frame.zig");
|
||||
|
||||
const Node = @import("../../Node.zig");
|
||||
const Element = @import("../../Element.zig");
|
||||
const AnimatedString = @import("../../svg/AnimatedString.zig");
|
||||
|
||||
const Graphics = @import("Graphics.zig");
|
||||
|
||||
const Use = @This();
|
||||
_proto: *Graphics,
|
||||
|
||||
pub fn asElement(self: *Use) *Element {
|
||||
return self._proto.asElement();
|
||||
}
|
||||
pub fn asNode(self: *Use) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Use);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGUseElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const href = bridge.accessor(_href, null, .{});
|
||||
fn _href(self: *Use, frame: *Frame) !*AnimatedString {
|
||||
return AnimatedString.getOrCreate(self.asElement(), .href, frame);
|
||||
}
|
||||
};
|
||||
84
src/browser/webapi/svg/AnimatedString.zig
Normal file
84
src/browser/webapi/svg/AnimatedString.zig
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
const Frame = @import("../../Frame.zig");
|
||||
const Element = @import("../Element.zig");
|
||||
|
||||
pub const Lookup = std.AutoHashMapUnmanaged(Key, *AnimatedString);
|
||||
|
||||
const String = lp.String;
|
||||
|
||||
const AnimatedString = @This();
|
||||
|
||||
_kind: Kind,
|
||||
_element: *Element,
|
||||
|
||||
pub const Kind = enum { class, href };
|
||||
pub const Key = struct {
|
||||
element: *Element,
|
||||
kind: Kind,
|
||||
};
|
||||
|
||||
// Identity map for AnimatedString, help by the frame
|
||||
pub fn getOrCreate(element: *Element, kind: Kind, frame: *Frame) !*AnimatedString {
|
||||
const gop = try frame._svg_animated_strings.getOrPut(frame.arena, .{ .element = element, .kind = kind });
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = try frame._factory.create(AnimatedString{
|
||||
._element = element,
|
||||
._kind = kind,
|
||||
});
|
||||
}
|
||||
return gop.value_ptr.*;
|
||||
}
|
||||
|
||||
pub fn getBaseVal(self: *const AnimatedString) []const u8 {
|
||||
return self._element.getAttributeSafe(self.attributeName()) orelse "";
|
||||
}
|
||||
|
||||
pub fn setBaseVal(self: *AnimatedString, value: String, frame: *Frame) !void {
|
||||
try self._element.setAttribute(self.attributeName(), value, frame);
|
||||
}
|
||||
|
||||
// No real animation, return the BaseVal
|
||||
pub fn getAnimVal(self: *const AnimatedString) []const u8 {
|
||||
return self.getBaseVal();
|
||||
}
|
||||
|
||||
fn attributeName(self: *const AnimatedString) String {
|
||||
return switch (self._kind) {
|
||||
.href => comptime .wrap("href"),
|
||||
.class => comptime .wrap("class"),
|
||||
};
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(AnimatedString);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGAnimatedString";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const baseVal = bridge.accessor(AnimatedString.getBaseVal, AnimatedString.setBaseVal, .{});
|
||||
pub const animVal = bridge.accessor(AnimatedString.getAnimVal, null, .{});
|
||||
};
|
||||
42
src/browser/webapi/svg/Number.zig
Normal file
42
src/browser/webapi/svg/Number.zig
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// 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 js = @import("../../js/js.zig");
|
||||
|
||||
const Number = @This();
|
||||
_value: f32 = 0,
|
||||
|
||||
pub fn getValue(self: *const Number) f32 {
|
||||
return self._value;
|
||||
}
|
||||
|
||||
pub fn setValue(self: *Number, value: f32) void {
|
||||
self._value = value;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Number);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "SVGNumber";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const value = bridge.accessor(Number.getValue, Number.setValue, .{});
|
||||
};
|
||||
Reference in New Issue
Block a user