1616// You should have received a copy of the GNU Affero General Public License
1717// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818const std = @import ("std" );
19+ const parser = @import ("../netsurf.zig" );
1920const Page = @import ("../page.zig" ).Page ;
21+
2022const Allocator = std .mem .Allocator ;
2123
2224const DataSet = @This ();
2325
24- attributes : std .StringHashMapUnmanaged ([]const u8 ),
25-
26- pub const empty : DataSet = .{
27- .attributes = .empty ,
28- };
26+ element : * parser.Element ,
2927
3028const GetResult = union (enum ) {
3129 value : []const u8 ,
3230 undefined : void ,
3331};
34- pub fn named_get (self : * const DataSet , name : []const u8 , _ : * bool ) GetResult {
35- if (self .attributes .get (name )) | value | {
32+ pub fn named_get (self : * const DataSet , name : []const u8 , _ : * bool , page : * Page ) ! GetResult {
33+ const normalized_name = try normalize (page .call_arena , name );
34+ if (try parser .elementGetAttribute (self .element , normalized_name )) | value | {
3635 return .{ .value = value };
3736 }
3837 return .{ .undefined = {} };
3938}
4039
4140pub fn named_set (self : * DataSet , name : []const u8 , value : []const u8 , _ : * bool , page : * Page ) ! void {
42- const arena = page .arena ;
43- const gop = try self .attributes .getOrPut (arena , name );
44- errdefer _ = self .attributes .remove (name );
45-
46- if (! gop .found_existing ) {
47- gop .key_ptr .* = try arena .dupe (u8 , name );
48- }
49- gop .value_ptr .* = try arena .dupe (u8 , value );
41+ const normalized_name = try normalize (page .call_arena , name );
42+ try parser .elementSetAttribute (self .element , normalized_name , value );
5043}
5144
52- pub fn named_delete (self : * DataSet , name : []const u8 , _ : * bool ) void {
53- _ = self .attributes .remove (name );
45+ pub fn named_delete (self : * DataSet , name : []const u8 , _ : * bool , page : * Page ) ! void {
46+ const normalized_name = try normalize (page .call_arena , name );
47+ try parser .elementRemoveAttribute (self .element , normalized_name );
5448}
5549
56- pub fn normalizeName (allocator : Allocator , name : []const u8 ) ! []const u8 {
57- std .debug .assert (std .mem .startsWith (u8 , name , "data-" ));
58- var owned = try allocator .alloc (u8 , name .len - 5 );
59-
60- var pos : usize = 0 ;
61- var capitalize = false ;
62- for (name [5.. ]) | c | {
63- if (c == '-' ) {
64- capitalize = true ;
65- continue ;
50+ fn normalize (allocator : Allocator , name : []const u8 ) ! []const u8 {
51+ var upper_count : usize = 0 ;
52+ for (name ) | c | {
53+ if (std .ascii .isUpper (c )) {
54+ upper_count += 1 ;
6655 }
56+ }
57+ // for every upper-case letter, we'll probably need a dash before it
58+ // and we need the 'data-' prefix
59+ var normalized = try allocator .alloc (u8 , name .len + upper_count + 5 );
6760
68- if (capitalize ) {
69- capitalize = false ;
70- owned [pos ] = std .ascii .toUpper (c );
61+ @memcpy (normalized [0.. 5], "data-" );
62+ if (upper_count == 0 ) {
63+ @memcpy (normalized [5.. ], name );
64+ return normalized ;
65+ }
66+
67+ var pos : usize = 5 ;
68+ for (name ) | c | {
69+ if (std .ascii .isUpper (c )) {
70+ normalized [pos ] = '-' ;
71+ pos += 1 ;
72+ normalized [pos ] = c + 32 ;
7173 } else {
72- owned [pos ] = c ;
74+ normalized [pos ] = c ;
7375 }
7476 pos += 1 ;
7577 }
76- return owned [0 .. pos ] ;
78+ return normalized ;
7779}
7880
7981const testing = @import ("../../testing.zig" );
@@ -88,5 +90,11 @@ test "Browser.HTML.DataSet" {
8890 .{ "delete el1.dataset.x" , "true" },
8991 .{ "el1.dataset.x" , "undefined" },
9092 .{ "delete el1.dataset.other" , "true" }, // yes, this is right
93+
94+ .{ "let ds1 = el1.dataset" , null },
95+ .{ "ds1.helloWorld = 'yes'" , null },
96+ .{ "el1.getAttribute('data-hello-world')" , "yes" },
97+ .{ "el1.setAttribute('data-this-will-work', 'positive')" , null },
98+ .{ "ds1.thisWillWork" , "positive" },
9199 }, .{});
92100}
0 commit comments