diff --git a/src/html/document.zig b/src/html/document.zig index be2a9f0c..deee09c3 100644 --- a/src/html/document.zig +++ b/src/html/document.zig @@ -57,38 +57,6 @@ pub const HTMLDocument = struct { // Tests // ----- -fn upper(comptime name: []const u8, comptime indexes: anytype) []u8 { - // indexes is [_]comptime_int - comptime { - var upper_name: [name.len]u8 = undefined; - for (name) |char, i| { - var toUpper = false; - for (indexes) |index| { - if (index == i) { - toUpper = true; - break; - } - } - if (toUpper) { - upper_name[i] = std.ascii.toUpper(char); - } else { - upper_name[i] = char; - } - } - return &upper_name; - } -} - -// fn allUpper(comptime name: []const u8) []u8 { -// comptime { -// var upper_name: [name.len]u8 = undefined; -// for (name) |char, i| { -// upper_name[i] = std.ascii.toUpper(char); -// } -// return &upper_name; -// } -// } - pub fn testExecFn( alloc: std.mem.Allocator, js_env: *jsruntime.Env, @@ -109,149 +77,29 @@ pub fn testExecFn( }; try checkCases(js_env, &getElementById); - comptime var htmlElements = [_][]const u8{ - "a", // Anchor - "area", - "audio", - "br", // BR - "base", - "body", - "button", - "canvas", - "dl", // DList - "dialog", - "data", - "div", - "embed", - "fieldset", // FieldSet - "form", - "frameset", // FrameSet - "hr", // HR - "head", - "h1", // Heading - "h2", // Heading - "h3", // Heading - "h4", // Heading - "h5", // Heading - "h6", // Heading - "html", - "iframe", // IFrame - "img", // Image - "input", - "li", // LI - "label", - "legend", - "link", - "map", - "meta", - "meter", - "ins", // Mod - "del", // Mod - "ol", // OList - "object", - "optgroup", // OptGroup - "option", - "output", - "p", // Paragraph - "picture", - "pre", - "progress", - "blockquote", // Quote - "q", // Quote - "script", - "select", - "source", - "span", - "style", - "table", - "caption", // TableCaption - "th", // TableCell - "td", // TableCell - "col", // TableCol - "tr", // TableRow - "thead", // TableSection - "tbody", // TableSection - "tfoot", // TableSection - "template", - "textarea", // TextArea - "time", - "title", - "track", - "ul", // UList - "video", - }; - var createElement: [htmlElements.len * 3]Case = undefined; - inline for (htmlElements) |elem, i| { - var upperName: []const u8 = undefined; - if (std.mem.eql(u8, elem, "a")) { - upperName = "Anchor"; - } else if (std.mem.eql(u8, elem, "dl")) { - upperName = "DList"; - } else if (std.mem.eql(u8, elem, "fieldset")) { - upperName = "FieldSet"; - } else if (std.mem.eql(u8, elem, "frameset")) { - upperName = "FrameSet"; - } else if (std.mem.eql(u8, elem, "h1") or - std.mem.eql(u8, elem, "h2") or - std.mem.eql(u8, elem, "h3") or - std.mem.eql(u8, elem, "h4") or - std.mem.eql(u8, elem, "h5") or - std.mem.eql(u8, elem, "h6")) - { - upperName = "Heading"; - } else if (std.mem.eql(u8, elem, "iframe")) { - upperName = "IFrame"; - } else if (std.mem.eql(u8, elem, "img")) { - upperName = "Image"; - } else if (std.mem.eql(u8, elem, "del") or std.mem.eql(u8, elem, "ins")) { - upperName = "Mod"; - } else if (std.mem.eql(u8, elem, "ol")) { - upperName = "OList"; - } else if (std.mem.eql(u8, elem, "optgroup")) { - upperName = "OptGroup"; - } else if (std.mem.eql(u8, elem, "p")) { - upperName = "Paragraph"; - } else if (std.mem.eql(u8, elem, "blockquote") or std.mem.eql(u8, elem, "q")) { - upperName = "Quote"; - } else if (std.mem.eql(u8, elem, "caption")) { - upperName = "TableCaption"; - } else if (std.mem.eql(u8, elem, "th") or std.mem.eql(u8, elem, "td")) { - upperName = "TableCell"; - } else if (std.mem.eql(u8, elem, "col")) { - upperName = "TableCol"; - } else if (std.mem.eql(u8, elem, "tr")) { - upperName = "TableRow"; - } else if (std.mem.eql(u8, elem, "thead") or - std.mem.eql(u8, elem, "tbody") or - std.mem.eql(u8, elem, "tfoot")) - { - upperName = "TableSection"; - } else if (std.mem.eql(u8, elem, "textarea")) { - upperName = "TextArea"; - } else if (std.mem.eql(u8, elem, "ul")) { - upperName = "UList"; - } else { - if (elem.len == 2) { - upperName = upper(elem, [_]comptime_int{ 0, 1 }); - } else { - upperName = upper(elem, [_]comptime_int{0}); - } + const tags = comptime parser.Tag.all(); + const elements = comptime parser.Tag.allElements(); + var createElements: [(tags.len - 1) * 3]Case = undefined; + inline for (tags) |tag, i| { + if (tag == .undef) { + continue; } - - createElement[i * 3] = Case{ - .src = try std.fmt.allocPrint(alloc, "var {s}Elem = document.createElement('{s}')", .{ elem, elem }), + const tag_name = @tagName(tag); + const element_name = elements[i]; + createElements[i * 3] = Case{ + .src = try std.fmt.allocPrint(alloc, "var {s}Elem = document.createElement('{s}')", .{ tag_name, tag_name }), .ex = "undefined", }; - createElement[(i * 3) + 1] = Case{ - .src = try std.fmt.allocPrint(alloc, "{s}Elem.constructor.name", .{elem}), - .ex = try std.fmt.allocPrint(alloc, "HTML{s}Element", .{upperName}), + createElements[(i * 3) + 1] = Case{ + .src = try std.fmt.allocPrint(alloc, "{s}Elem.constructor.name", .{tag_name}), + .ex = try std.fmt.allocPrint(alloc, "HTML{s}Element", .{element_name}), }; - createElement[(i * 3) + 2] = Case{ - .src = try std.fmt.allocPrint(alloc, "{s}Elem.localName", .{elem}), - .ex = elem, + createElements[(i * 3) + 2] = Case{ + .src = try std.fmt.allocPrint(alloc, "{s}Elem.localName", .{tag_name}), + .ex = tag_name, }; } - try checkCases(js_env, &createElement); + try checkCases(js_env, &createElements); var unknown = [_]Case{ .{ .src = "let unknown = document.createElement('unknown')", .ex = "undefined" }, diff --git a/src/html/elements.zig b/src/html/elements.zig index a8804a04..a44973cd 100644 --- a/src/html/elements.zig +++ b/src/html/elements.zig @@ -762,82 +762,68 @@ pub const HTMLVideoElement = struct { } }; -const c = @cImport({ - @cInclude("lexbor/html/html.h"); -}); - pub fn ElementToHTMLElementInterface(base: *parser.Element) HTMLElements { - return switch (base.*.node.local_name) { - c.LXB_TAG_A => .{ .anchor = HTMLAnchorElement.init(base) }, - c.LXB_TAG_AREA => .{ .area = HTMLAreaElement.init(base) }, - c.LXB_TAG_AUDIO => .{ .audio = HTMLAudioElement.init(base) }, - c.LXB_TAG_BR => .{ .br = HTMLBRElement.init(base) }, - c.LXB_TAG_BASE => .{ .base = HTMLBaseElement.init(base) }, - c.LXB_TAG_BODY => .{ .body = HTMLBodyElement.init(base) }, - c.LXB_TAG_BUTTON => .{ .button = HTMLButtonElement.init(base) }, - c.LXB_TAG_CANVAS => .{ .canvas = HTMLCanvasElement.init(base) }, - c.LXB_TAG_DL => .{ .dlist = HTMLDListElement.init(base) }, - c.LXB_TAG_DIALOG => .{ .dialog = HTMLDialogElement.init(base) }, - c.LXB_TAG_DATA => .{ .data = HTMLDataElement.init(base) }, - c.LXB_TAG_DIV => .{ .div = HTMLDivElement.init(base) }, - c.LXB_TAG_EMBED => .{ .embed = HTMLEmbedElement.init(base) }, - c.LXB_TAG_FIELDSET => .{ .fieldset = HTMLFieldSetElement.init(base) }, - c.LXB_TAG_FORM => .{ .form = HTMLFormElement.init(base) }, - c.LXB_TAG_FRAMESET => .{ .frameset = HTMLFrameSetElement.init(base) }, - c.LXB_TAG_HR => .{ .hr = HTMLHRElement.init(base) }, - c.LXB_TAG_HEAD => .{ .head = HTMLHeadElement.init(base) }, - c.LXB_TAG_H1 => .{ .heading = HTMLHeadingElement.init(base) }, - c.LXB_TAG_H2 => .{ .heading = HTMLHeadingElement.init(base) }, - c.LXB_TAG_H3 => .{ .heading = HTMLHeadingElement.init(base) }, - c.LXB_TAG_H4 => .{ .heading = HTMLHeadingElement.init(base) }, - c.LXB_TAG_H5 => .{ .heading = HTMLHeadingElement.init(base) }, - c.LXB_TAG_H6 => .{ .heading = HTMLHeadingElement.init(base) }, - c.LXB_TAG_HTML => .{ .html = HTMLHtmlElement.init(base) }, - c.LXB_TAG_IFRAME => .{ .iframe = HTMLIFrameElement.init(base) }, - c.LXB_TAG_IMG => .{ .img = HTMLImageElement.init(base) }, - c.LXB_TAG_INPUT => .{ .input = HTMLInputElement.init(base) }, - c.LXB_TAG_LI => .{ .li = HTMLLIElement.init(base) }, - c.LXB_TAG_LABEL => .{ .label = HTMLLabelElement.init(base) }, - c.LXB_TAG_LEGEND => .{ .legend = HTMLLegendElement.init(base) }, - c.LXB_TAG_LINK => .{ .link = HTMLLinkElement.init(base) }, - c.LXB_TAG_MAP => .{ .map = HTMLMapElement.init(base) }, - c.LXB_TAG_META => .{ .meta = HTMLMetaElement.init(base) }, - c.LXB_TAG_METER => .{ .meter = HTMLMeterElement.init(base) }, - c.LXB_TAG_INS => .{ .mod = HTMLModElement.init(base) }, - c.LXB_TAG_DEL => .{ .mod = HTMLModElement.init(base) }, - c.LXB_TAG_OL => .{ .olist = HTMLOListElement.init(base) }, - c.LXB_TAG_OBJECT => .{ .object = HTMLObjectElement.init(base) }, - c.LXB_TAG_OPTGROUP => .{ .optgroup = HTMLOptGroupElement.init(base) }, - c.LXB_TAG_OPTION => .{ .option = HTMLOptionElement.init(base) }, - c.LXB_TAG_OUTPUT => .{ .output = HTMLOutputElement.init(base) }, - c.LXB_TAG_P => .{ .paragraph = HTMLParagraphElement.init(base) }, - c.LXB_TAG_PICTURE => .{ .picture = HTMLPictureElement.init(base) }, - c.LXB_TAG_PRE => .{ .pre = HTMLPreElement.init(base) }, - c.LXB_TAG_PROGRESS => .{ .progress = HTMLProgressElement.init(base) }, - c.LXB_TAG_BLOCKQUOTE => .{ .quote = HTMLQuoteElement.init(base) }, - c.LXB_TAG_Q => .{ .quote = HTMLQuoteElement.init(base) }, - c.LXB_TAG_SCRIPT => .{ .script = HTMLScriptElement.init(base) }, - c.LXB_TAG_SELECT => .{ .select = HTMLSelectElement.init(base) }, - c.LXB_TAG_SOURCE => .{ .source = HTMLSourceElement.init(base) }, - c.LXB_TAG_SPAN => .{ .span = HTMLSpanElement.init(base) }, - c.LXB_TAG_STYLE => .{ .style = HTMLStyleElement.init(base) }, - c.LXB_TAG_TABLE => .{ .table = HTMLTableElement.init(base) }, - c.LXB_TAG_CAPTION => .{ .tablecaption = HTMLTableCaptionElement.init(base) }, - c.LXB_TAG_TH => .{ .tablecell = HTMLTableCellElement.init(base) }, - c.LXB_TAG_TD => .{ .tablecell = HTMLTableCellElement.init(base) }, - c.LXB_TAG_COL => .{ .tablecol = HTMLTableColElement.init(base) }, - c.LXB_TAG_TR => .{ .tablerow = HTMLTableRowElement.init(base) }, - c.LXB_TAG_THEAD => .{ .tablesection = HTMLTableSectionElement.init(base) }, - c.LXB_TAG_TBODY => .{ .tablesection = HTMLTableSectionElement.init(base) }, - c.LXB_TAG_TFOOT => .{ .tablesection = HTMLTableSectionElement.init(base) }, - c.LXB_TAG_TEMPLATE => .{ .template = HTMLTemplateElement.init(base) }, - c.LXB_TAG_TEXTAREA => .{ .textarea = HTMLTextAreaElement.init(base) }, - c.LXB_TAG_TIME => .{ .time = HTMLTimeElement.init(base) }, - c.LXB_TAG_TITLE => .{ .title = HTMLTitleElement.init(base) }, - c.LXB_TAG_TRACK => .{ .track = HTMLTrackElement.init(base) }, - c.LXB_TAG_UL => .{ .ulist = HTMLUListElement.init(base) }, - c.LXB_TAG_VIDEO => .{ .video = HTMLVideoElement.init(base) }, - c.LXB_TAG__UNDEF => .{ .unknown = HTMLUnknownElement.init(base) }, - else => .{ .unknown = HTMLUnknownElement.init(base) }, + const tag = parser.nodeTag(parser.elementNode(base)); + return switch (tag) { + .a => .{ .anchor = HTMLAnchorElement.init(base) }, + .area => .{ .area = HTMLAreaElement.init(base) }, + .audio => .{ .audio = HTMLAudioElement.init(base) }, + .br => .{ .br = HTMLBRElement.init(base) }, + .base => .{ .base = HTMLBaseElement.init(base) }, + .body => .{ .body = HTMLBodyElement.init(base) }, + .button => .{ .button = HTMLButtonElement.init(base) }, + .canvas => .{ .canvas = HTMLCanvasElement.init(base) }, + .dl => .{ .dlist = HTMLDListElement.init(base) }, + .dialog => .{ .dialog = HTMLDialogElement.init(base) }, + .data => .{ .data = HTMLDataElement.init(base) }, + .div => .{ .div = HTMLDivElement.init(base) }, + .embed => .{ .embed = HTMLEmbedElement.init(base) }, + .fieldset => .{ .fieldset = HTMLFieldSetElement.init(base) }, + .form => .{ .form = HTMLFormElement.init(base) }, + .frameset => .{ .frameset = HTMLFrameSetElement.init(base) }, + .hr => .{ .hr = HTMLHRElement.init(base) }, + .head => .{ .head = HTMLHeadElement.init(base) }, + .h1, .h2, .h3, .h4, .h5, .h6 => .{ .heading = HTMLHeadingElement.init(base) }, + .html => .{ .html = HTMLHtmlElement.init(base) }, + .iframe => .{ .iframe = HTMLIFrameElement.init(base) }, + .img => .{ .img = HTMLImageElement.init(base) }, + .input => .{ .input = HTMLInputElement.init(base) }, + .li => .{ .li = HTMLLIElement.init(base) }, + .label => .{ .label = HTMLLabelElement.init(base) }, + .legend => .{ .legend = HTMLLegendElement.init(base) }, + .link => .{ .link = HTMLLinkElement.init(base) }, + .map => .{ .map = HTMLMapElement.init(base) }, + .meta => .{ .meta = HTMLMetaElement.init(base) }, + .meter => .{ .meter = HTMLMeterElement.init(base) }, + .ins, .del => .{ .mod = HTMLModElement.init(base) }, + .ol => .{ .olist = HTMLOListElement.init(base) }, + .object => .{ .object = HTMLObjectElement.init(base) }, + .optgroup => .{ .optgroup = HTMLOptGroupElement.init(base) }, + .option => .{ .option = HTMLOptionElement.init(base) }, + .output => .{ .output = HTMLOutputElement.init(base) }, + .p => .{ .paragraph = HTMLParagraphElement.init(base) }, + .picture => .{ .picture = HTMLPictureElement.init(base) }, + .pre => .{ .pre = HTMLPreElement.init(base) }, + .progress => .{ .progress = HTMLProgressElement.init(base) }, + .blockquote, .q => .{ .quote = HTMLQuoteElement.init(base) }, + .script => .{ .script = HTMLScriptElement.init(base) }, + .select => .{ .select = HTMLSelectElement.init(base) }, + .source => .{ .source = HTMLSourceElement.init(base) }, + .span => .{ .span = HTMLSpanElement.init(base) }, + .style => .{ .style = HTMLStyleElement.init(base) }, + .table => .{ .table = HTMLTableElement.init(base) }, + .caption => .{ .tablecaption = HTMLTableCaptionElement.init(base) }, + .th, .td => .{ .tablecell = HTMLTableCellElement.init(base) }, + .col => .{ .tablecol = HTMLTableColElement.init(base) }, + .tr => .{ .tablerow = HTMLTableRowElement.init(base) }, + .thead, .tbody, .tfoot => .{ .tablesection = HTMLTableSectionElement.init(base) }, + .template => .{ .template = HTMLTemplateElement.init(base) }, + .textarea => .{ .textarea = HTMLTextAreaElement.init(base) }, + .time => .{ .time = HTMLTimeElement.init(base) }, + .title => .{ .title = HTMLTitleElement.init(base) }, + .track => .{ .track = HTMLTrackElement.init(base) }, + .ul => .{ .ulist = HTMLUListElement.init(base) }, + .video => .{ .video = HTMLVideoElement.init(base) }, + .undef => .{ .unknown = HTMLUnknownElement.init(base) }, }; } diff --git a/src/parser.zig b/src/parser.zig index 20df1766..f018a230 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -7,6 +7,149 @@ const c = @cImport({ // Public API // ---------- +// Tag + +pub const Tag = enum(u8) { + a = c.LXB_TAG_A, + area = c.LXB_TAG_AREA, + audio = c.LXB_TAG_AUDIO, + br = c.LXB_TAG_BR, + base = c.LXB_TAG_BASE, + body = c.LXB_TAG_BODY, + button = c.LXB_TAG_BUTTON, + canvas = c.LXB_TAG_CANVAS, + dl = c.LXB_TAG_DL, + dialog = c.LXB_TAG_DIALOG, + data = c.LXB_TAG_DATA, + div = c.LXB_TAG_DIV, + embed = c.LXB_TAG_EMBED, + fieldset = c.LXB_TAG_FIELDSET, + form = c.LXB_TAG_FORM, + frameset = c.LXB_TAG_FRAMESET, + hr = c.LXB_TAG_HR, + head = c.LXB_TAG_HEAD, + h1 = c.LXB_TAG_H1, + h2 = c.LXB_TAG_H2, + h3 = c.LXB_TAG_H3, + h4 = c.LXB_TAG_H4, + h5 = c.LXB_TAG_H5, + h6 = c.LXB_TAG_H6, + html = c.LXB_TAG_HTML, + iframe = c.LXB_TAG_IFRAME, + img = c.LXB_TAG_IMG, + input = c.LXB_TAG_INPUT, + li = c.LXB_TAG_LI, + label = c.LXB_TAG_LABEL, + legend = c.LXB_TAG_LEGEND, + link = c.LXB_TAG_LINK, + map = c.LXB_TAG_MAP, + meta = c.LXB_TAG_META, + meter = c.LXB_TAG_METER, + ins = c.LXB_TAG_INS, + del = c.LXB_TAG_DEL, + ol = c.LXB_TAG_OL, + object = c.LXB_TAG_OBJECT, + optgroup = c.LXB_TAG_OPTGROUP, + option = c.LXB_TAG_OPTION, + output = c.LXB_TAG_OUTPUT, + p = c.LXB_TAG_P, + picture = c.LXB_TAG_PICTURE, + pre = c.LXB_TAG_PRE, + progress = c.LXB_TAG_PROGRESS, + blockquote = c.LXB_TAG_BLOCKQUOTE, + q = c.LXB_TAG_Q, + script = c.LXB_TAG_SCRIPT, + select = c.LXB_TAG_SELECT, + source = c.LXB_TAG_SOURCE, + span = c.LXB_TAG_SPAN, + style = c.LXB_TAG_STYLE, + table = c.LXB_TAG_TABLE, + caption = c.LXB_TAG_CAPTION, + th = c.LXB_TAG_TH, + td = c.LXB_TAG_TD, + col = c.LXB_TAG_COL, + tr = c.LXB_TAG_TR, + thead = c.LXB_TAG_THEAD, + tbody = c.LXB_TAG_TBODY, + tfoot = c.LXB_TAG_TFOOT, + template = c.LXB_TAG_TEMPLATE, + textarea = c.LXB_TAG_TEXTAREA, + time = c.LXB_TAG_TIME, + title = c.LXB_TAG_TITLE, + track = c.LXB_TAG_TRACK, + ul = c.LXB_TAG_UL, + video = c.LXB_TAG_VIDEO, + undef = c.LXB_TAG__UNDEF, + + pub fn all() []Tag { + comptime { + const info = @typeInfo(Tag).Enum; + comptime var l: [info.fields.len]Tag = undefined; + inline for (info.fields) |field, i| { + l[i] = @intToEnum(Tag, field.value); + } + return &l; + } + } + + pub fn allElements() [][]const u8 { + comptime { + const tags = all(); + var names: [tags.len][]const u8 = undefined; + inline for (tags) |tag, i| { + names[i] = tag.elementName(); + } + return &names; + } + } + + fn upperName(comptime name: []const u8) []const u8 { + comptime { + var upper_name: [name.len]u8 = undefined; + for (name) |char, i| { + var to_upper = false; + if (i == 0) { + to_upper = true; + } else if (i == 1 and name.len == 2) { + to_upper = true; + } + if (to_upper) { + upper_name[i] = std.ascii.toUpper(char); + } else { + upper_name[i] = char; + } + } + return &upper_name; + } + } + + fn elementName(comptime tag: Tag) []const u8 { + return switch (tag) { + .area, .audio, .base, .body, .button, .br, .canvas, .dialog, .data, .div, .embed, .form, .head, .html, .hr, .input, .label, .li, .legend, .link, .map, .meta, .meter, .object, .option, .output, .picture, .pre, .progress, .script, .select, .source, .span, .style, .table, .template, .time, .title, .track, .video => upperName(@tagName(tag)), + .a => "Anchor", + .dl => "DList", + .fieldset => "FieldSet", + .frameset => "FrameSet", + .h1, .h2, .h3, .h4, .h5, .h6 => "Heading", + .iframe => "IFrame", + .img => "Image", + .ins, .del => "Mod", + .ol => "OList", + .optgroup => "OptGroup", + .p => "Paragraph", + .blockquote, .q => "Quote", + .caption => "TableCaption", + .th, .td => "TableCell", + .col => "TableCol", + .tr => "TableRow", + .thead, .tbody, .tfoot => "TableSection", + .textarea => "TextArea", + .ul => "UList", + .undef => "Unknown", + }; + } +}; + // EventTarget pub const EventTarget = c.lxb_dom_event_target_t; @@ -36,6 +179,10 @@ pub inline fn nodeEventTarget(node: *Node) *EventTarget { return c.lxb_dom_interface_event_target(node); } +pub inline fn nodeTag(node: *Node) Tag { + return @intToEnum(Tag, c.lxb_dom_node_tag_id(node)); +} + pub const nodeWalker = (fn (node: ?*Node, _: ?*anyopaque) callconv(.C) Action); pub inline fn nodeName(node: *Node) [*c]const u8 {