diff options
author | bptato <nincsnevem662@gmail.com> | 2022-11-28 19:52:10 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-11-28 23:00:06 +0100 |
commit | eb2e57c97eb67eec19f068e294a8f6d1375c82f5 (patch) | |
tree | 87156c515f6ee9a63f58dc080184bd3127ce6836 /src/html | |
parent | 8af10b8b74fd29fe4c9debcd5cbecfaddf53a7b5 (diff) | |
download | chawan-eb2e57c97eb67eec19f068e294a8f6d1375c82f5.tar.gz |
Add textarea
Editing is implemented using an external editor (like vi).
Diffstat (limited to 'src/html')
-rw-r--r-- | src/html/dom.nim | 157 | ||||
-rw-r--r-- | src/html/htmlparser.nim | 74 | ||||
-rw-r--r-- | src/html/tags.nim | 2 |
3 files changed, 154 insertions, 79 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim index be66dc51..ab3b95be 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -1,3 +1,4 @@ +import macros import options import streams import strutils @@ -91,10 +92,10 @@ type HTMLElement* = ref object of Element FormAssociatedElement* = ref object of HTMLElement - form*: HTMLFormElement parserInserted*: bool HTMLInputElement* = ref object of FormAssociatedElement + form* {.jsget.}: HTMLFormElement inputType*: InputType autofocus*: bool required*: bool @@ -108,6 +109,7 @@ type HTMLAnchorElement* = ref object of HTMLElement HTMLSelectElement* = ref object of FormAssociatedElement + form* {.jsget.}: HTMLFormElement size*: int HTMLSpanElement* = ref object of HTMLElement @@ -169,8 +171,15 @@ type HTMLAreaElement* = ref object of HTMLElement HTMLButtonElement* = ref object of FormAssociatedElement + form* {.jsget.}: HTMLFormElement ctype*: ButtonType - value*: string + value* {.jsget, jsset.}: string + + HTMLTextAreaElement* = ref object of FormAssociatedElement + form* {.jsget.}: HTMLFormElement + rows*: int + cols*: int + value* {.jsget.}: string proc tostr(ftype: enum): string = return ($ftype).split('_')[1..^1].join("-").tolower() @@ -312,6 +321,39 @@ iterator options*(select: HTMLSelectElement): HTMLOptionElement {.inline.} = if opt.tagType == TAG_OPTION: yield HTMLOptionElement(child) +func form*(element: FormAssociatedElement): HTMLFormElement = + case element.tagType + of TAG_INPUT: return HTMLInputElement(element).form + of TAG_SELECT: return HTMLSelectElement(element).form + of TAG_BUTTON: return HTMLButtonElement(element).form + of TAG_TEXTAREA: return HTMLTextAreaElement(element).form + else: assert false + +func `form=`*(element: FormAssociatedElement, form: HTMLFormElement) = + case element.tagType + of TAG_INPUT: HTMLInputElement(element).form = form + of TAG_SELECT: HTMLSelectElement(element).form = form + of TAG_BUTTON: HTMLButtonElement(element).form = form + of TAG_TEXTAREA: HTMLTextAreaElement(element).form = form + else: assert false + +func canSubmitImplicitly*(form: HTMLFormElement): bool = + const BlocksImplicitSubmission = { + INPUT_TEXT, INPUT_SEARCH, INPUT_URL, INPUT_TEL, INPUT_EMAIL, INPUT_PASSWORD, + INPUT_DATE, INPUT_MONTH, INPUT_WEEK, INPUT_TIME, INPUT_DATETIME_LOCAL, + INPUT_NUMBER + } + var found = false + for control in form.controls: + if control.tagType == TAG_INPUT: + let input = HTMLInputElement(control) + if input.inputType in BlocksImplicitSubmission: + if found: + return false + else: + found = true + return true + func qualifiedName*(element: Element): string = if element.namespacePrefix.issome: element.namespacePrefix.get & ':' & element.localName else: element.localName @@ -441,7 +483,7 @@ func nextElementSibling*(elem: Element): Element = inc i return nil -func attr*(element: Element, s: string): string = +func attr*(element: Element, s: string): string {.inline.} = return element.attributes.getOrDefault(s, "") func attri*(element: Element, s: string): Option[int] = @@ -492,7 +534,7 @@ proc sheets*(element: Element): seq[CSSStylesheet] = result.add(child.sheet) func inputString*(input: HTMLInputElement): string = - var text = case input.inputType + case input.inputType of INPUT_CHECKBOX, INPUT_RADIO: if input.checked: "*" else: " " @@ -510,7 +552,17 @@ func inputString*(input: HTMLInputElement): string = if input.file.isnone: "".padToWidth(input.size) else: input.file.get.path.serialize_unicode().padToWidth(input.size) else: input.value - return text + +func textAreaString*(textarea: HTMLTextAreaElement): string = + let split = textarea.value.split('\n') + for i in 0 ..< textarea.rows: + if textarea.cols > 2: + if i < split.len: + result &= '[' & split[i].padToWidth(textarea.cols - 2) & "]\n" + else: + result &= '[' & ' '.repeat(textarea.cols - 2) & "]\n" + else: + result &= "[]\n" func isButton*(element: Element): bool = if element.tagType == TAG_BUTTON: @@ -537,6 +589,8 @@ func action*(element: Element): string = if element.form != nil: if element.form.attrb("action"): return element.form.attr("action") + if element.tagType == TAG_FORM: + return element.attr("action") return "" func enctype*(element: Element): FormEncodingType = @@ -655,6 +709,8 @@ func newHTMLElement*(document: Document, tagType: TagType, namespace = Namespace result = new(HTMLBaseElement) of TAG_BUTTON: result = new(HTMLButtonElement) + of TAG_TEXTAREA: + result = new(HTMLTextAreaElement) else: result = new(HTMLElement) @@ -886,6 +942,10 @@ proc resetElement*(element: Element) = if option.selected: option.selected = false inc j + of TAG_TEXTAREA: + let textarea = HTMLTextAreaElement(element) + textarea.value = textarea.childTextContent() + textarea.invalid = true else: discard proc setForm*(element: FormAssociatedElement, form: HTMLFormElement) = @@ -902,7 +962,11 @@ proc setForm*(element: FormAssociatedElement, form: HTMLFormElement) = let button = HTMLButtonElement(element) button.form = form form.controls.add(button) - of TAG_FIELDSET, TAG_OBJECT, TAG_OUTPUT, TAG_TEXTAREA, TAG_IMG: + of TAG_TEXTAREA: + let textarea = HTMLTextAreaElement(element) + textarea.form = form + form.controls.add(textarea) + of TAG_FIELDSET, TAG_OBJECT, TAG_OUTPUT, TAG_IMG: discard #TODO else: assert false @@ -935,9 +999,7 @@ proc insertionSteps(insertedNode: Node) = if select != nil: select.resetElement() else: discard - if tagType in FormAssociatedElements: - if tagType notin SupportedFormAssociatedElements: - return #TODO TODO TODO implement others too + if tagType in SupportedFormAssociatedElements: let element = FormAssociatedElement(element) if element.parserInserted: return @@ -987,52 +1049,57 @@ proc reset*(form: HTMLFormElement) = control.resetElement() control.invalid = true -proc appendAttribute*(element: Element, k, v: string) = - case k - of "id": element.id = v - of "class": - let classes = v.split(' ') - for class in classes: - if class != "" and class notin element.classList: - element.classList.add(class) +proc appendAttributes*(element: Element, attrs: Table[string, string]) = + for k, v in attrs: + element.attributes[k] = v + template reflect_str(element: Element, name: static string, val: untyped) = + element.attributes.withValue(name, val): + element.val = val[] + template reflect_str(element: Element, name: static string, val, fun: untyped) = + element.attributes.withValue(name, val): + element.val = fun(val[]) + template reflect_nonzero_int(element: Element, name: static string, val: untyped, default: int) = + element.attributes.withValue(name, val): + if val[].isValidNonZeroInt(): + element.val = parseInt(val[]) + else: + element.val = default + do: + element.val = default + template reflect_bool(element: Element, name: static string, val: untyped) = + if name in element.attributes: + element.val = true + element.reflect_str "id", id + element.attributes.withValue("class", val): + let classList = val[].split(' ') + for x in classList: + if x != "" and x notin element.classList: + element.classList.add(x) case element.tagType of TAG_INPUT: let input = HTMLInputElement(element) - case k - of "value": input.value = v - of "type": input.inputType = inputType(v) - of "size": - if v.isValidNonZeroInt(): - input.size = parseInt(v) - else: - input.size = 20 - of "checked": input.checked = true + input.reflect_str "value", value + input.reflect_str "type", inputType, inputType + input.reflect_nonzero_int "size", size, 20 + input.reflect_bool "checked", checked of TAG_OPTION: let option = HTMLOptionElement(element) - if k == "selected": - option.selected = true + option.reflect_bool "selected", selected of TAG_SELECT: let select = HTMLSelectElement(element) - case k - of "multiple": - if not select.attributes["size"].isValidNonZeroInt(): - select.size = 4 - of "size": - if v.isValidNonZeroInt(): - select.size = parseInt(v) - elif "multiple" in select.attributes: - select.size = 4 + select.reflect_nonzero_int "size", size, (if "multiple" in element.attributes: 4 else: 1) of TAG_BUTTON: let button = HTMLButtonElement(element) - if k == "type": - case v - of "submit": button.ctype = BUTTON_SUBMIT - of "reset": button.ctype = BUTTON_RESET - of "button": button.ctype = BUTTON_BUTTON - elif k == "value": - button.value = v + button.reflect_str "type", ctype, (func(s: string): ButtonType = + case s + of "submit": return BUTTON_SUBMIT + of "reset": return BUTTON_RESET + of "button": return BUTTON_BUTTON) + of TAG_TEXTAREA: + let textarea = HTMLTextAreaElement(element) + textarea.reflect_nonzero_int "cols", cols, 20 + textarea.reflect_nonzero_int "rows", rows, 1 else: discard - element.attributes[k] = v # Forward definition hack (these are set in selectors.nim) var doqsa*: proc (node: Node, q: string): seq[Element] diff --git a/src/html/htmlparser.nim b/src/html/htmlparser.nim index 2b48c59d..e1359201 100644 --- a/src/html/htmlparser.nim +++ b/src/html/htmlparser.nim @@ -16,11 +16,13 @@ import utils/twtstr type DOMParser = ref object # JS interface + OpenElements = seq[Element] + HTML5Parser = object case fragment: bool of true: ctx: Element else: discard - openElements: seq[Element] + openElements: OpenElements insertionMode: InsertionMode oldInsertionMode: InsertionMode templateModes: seq[InsertionMode] @@ -196,8 +198,9 @@ func createElement(parser: HTML5Parser, token: Token, namespace: Namespace, inte let document = intendedParent.document let localName = token.tagname let element = document.newHTMLElement(localName, namespace, tagType = token.tagtype) - for k, v in token.attrs: - element.appendAttribute(k, v) + element.appendAttributes(token.attrs) + #for k, v in token.attrs: + # element.appendAttribute(k, v) if element.isResettable(): element.resetElement() @@ -450,18 +453,23 @@ proc genericRCDATAElementParsingAlgorithm(parser: var HTML5Parser, token: Token) parser.oldInsertionMode = parser.insertionMode parser.insertionMode = TEXT +proc popElement(parser: var HTML5Parser): Element = + result = parser.openElements.pop() + if result.tagType == TAG_TEXTAREA: + result.resetElement() + # 13.2.6.3 proc generateImpliedEndTags(parser: var HTML5Parser) = const tags = {TAG_DD, TAG_DT, TAG_LI, TAG_OPTGROUP, TAG_OPTION, TAG_P, TAG_RB, TAG_RP, TAG_RT, TAG_RTC} while parser.currentNode.tagType in tags: - discard parser.openElements.pop() + discard parser.popElement() proc generateImpliedEndTags(parser: var HTML5Parser, exclude: TagType) = let tags = {TAG_DD, TAG_DT, TAG_LI, TAG_OPTGROUP, TAG_OPTION, TAG_P, TAG_RB, TAG_RP, TAG_RT, TAG_RTC} - {exclude} while parser.currentNode.tagType in tags: - discard parser.openElements.pop() + discard parser.popElement() proc generateImpliedEndTagsThoroughly(parser: var HTML5Parser) = const tags = {TAG_CAPTION, TAG_COLGROUP, TAG_DD, TAG_DT, TAG_LI, @@ -469,7 +477,7 @@ proc generateImpliedEndTagsThoroughly(parser: var HTML5Parser) = TAG_RTC, TAG_TBODY, TAG_TD, TAG_TFOOT, TAG_TH, TAG_THEAD, TAG_TR} while parser.currentNode.tagType in tags: - discard parser.openElements.pop() + discard parser.popElement() # 13.2.4.3 proc pushOntoActiveFormatting(parser: var HTML5Parser, element: Element, token: Token) = @@ -535,7 +543,7 @@ proc reconstructActiveFormatting(parser: var HTML5Parser) = proc clearActiveFormattingTillMarker(parser: var HTML5Parser) = while parser.activeFormatting.len > 0 and parser.activeFormatting.pop()[0] != nil: discard -template pop_current_node = discard parser.openElements.pop() +template pop_current_node = discard parser.popElement() func isHTMLIntegrationPoint(node: Element): bool = return false #TODO SVG (NOTE MathML not implemented) @@ -849,7 +857,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = parser.generateImpliedEndTagsThoroughly() if parser.currentNode.tagType != TAG_TEMPLATE: parse_error - while parser.openElements.pop().tagType != TAG_TEMPLATE: discard + while parser.popElement().tagType != TAG_TEMPLATE: discard parser.clearActiveFormattingTillMarker() discard parser.templateModes.pop() parser.resetInsertionMode() @@ -918,7 +926,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = proc closeP(parser: var HTML5Parser) = parser.generateImpliedEndTags(TAG_P) if parser.currentNode.tagType != TAG_P: parse_error - while parser.openElements.pop().tagType != TAG_P: discard + while parser.popElement().tagType != TAG_P: discard proc adoptionAgencyAlgorithm(parser: var HTML5Parser, token: Token): bool = if parser.currentNode.tagType != TAG_UNKNOWN and parser.currentNode.tagtype == token.tagtype or parser.currentNode.localName == token.tagname: @@ -965,7 +973,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = furthestBlockIndex = j break if furthestBlock == nil: - while parser.openElements.pop() != formatting: discard + while parser.popElement() != formatting: discard parser.activeFormatting.delete(formattingIndex) return false let commonAncestor = parser.openElements[stackIndex - 1] @@ -1031,7 +1039,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = if node.tagType != TAG_UNKNOWN and node.tagType == token.tagtype or node.localName == token.tagname: parser.generateImpliedEndTags(token.tagtype) if node != parser.currentNode: parse_error - while parser.openElements.pop() != node: discard + while parser.popElement() != node: discard break elif node.tagType in SpecialElements: parse_error @@ -1149,7 +1157,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = of TAG_LI: parser.generateImpliedEndTags(TAG_LI) if parser.currentNode.tagType != TAG_LI: parse_error - while parser.openElements.pop().tagType != TAG_LI: discard + while parser.popElement().tagType != TAG_LI: discard break of SpecialElements - {TAG_ADDRESS, TAG_DIV, TAG_P, TAG_LI}: break @@ -1166,12 +1174,12 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = of TAG_DD: parser.generateImpliedEndTags(TAG_DD) if parser.currentNode.tagType != TAG_DD: parse_error - while parser.openElements.pop().tagType != TAG_DD: discard + while parser.popElement().tagType != TAG_DD: discard break of TAG_DT: parser.generateImpliedEndTags(TAG_DT) if parser.currentNode.tagType != TAG_DT: parse_error - while parser.openElements.pop().tagType != TAG_DT: discard + while parser.popElement().tagType != TAG_DT: discard break of SpecialElements - {TAG_ADDRESS, TAG_DIV, TAG_P, TAG_DD, TAG_DT}: break @@ -1190,7 +1198,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = if parser.openElements.hasElementInScope(TAG_BUTTON): parse_error parser.generateImpliedEndTags() - while parser.openElements.pop().tagType != TAG_BUTTON: discard + while parser.popElement().tagType != TAG_BUTTON: discard parser.reconstructActiveFormatting() discard parser.insertHTMLElement(token) parser.framesetOk = false @@ -1205,7 +1213,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = else: parser.generateImpliedEndTags() if parser.currentNode.tagType != token.tagtype: parse_error - while parser.openElements.pop().tagType != token.tagtype: discard + while parser.popElement().tagType != token.tagtype: discard ) "</form>" => (block: if not parser.openElements.hasElement(TAG_TEMPLATE): @@ -1223,7 +1231,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = return parser.generateImpliedEndTags() if parser.currentNode.tagType != TAG_FORM: parse_error - while parser.openElements.pop().tagType != TAG_FORM: discard + while parser.popElement().tagType != TAG_FORM: discard ) "</p>" => (block: if not parser.openElements.hasElementInButtonScope(TAG_P): @@ -1237,7 +1245,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = else: parser.generateImpliedEndTags(TAG_LI) if parser.currentNode.tagType != TAG_LI: parse_error - while parser.openElements.pop().tagType != TAG_LI: discard + while parser.popElement().tagType != TAG_LI: discard ) ("</dd>", "</dt>") => (block: if not parser.openElements.hasElementInScope(token.tagtype): @@ -1245,7 +1253,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = else: parser.generateImpliedEndTags(token.tagtype) if parser.currentNode.tagType != token.tagtype: parse_error - while parser.openElements.pop().tagType != token.tagtype: discard + while parser.popElement().tagType != token.tagtype: discard ) ("</h1>", "</h2>", "</h3>", "</h4>", "</h5>", "</h6>") => (block: if not parser.openElements.hasElementInScope(HTagTypes): @@ -1253,7 +1261,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = else: parser.generateImpliedEndTags() if parser.currentNode.tagType != token.tagtype: parse_error - while parser.openElements.pop().tagType notin HTagTypes: discard + while parser.popElement().tagType notin HTagTypes: discard ) "</sarcasm>" => (block: #*deep breath* @@ -1321,7 +1329,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = else: parser.generateImpliedEndTags() if parser.currentNode.tagType != token.tagtype: parse_error - while parser.openElements.pop().tagType != token.tagtype: discard + while parser.popElement().tagType != token.tagtype: discard parser.clearActiveFormattingTillMarker() ) "<table>" => (block: @@ -1504,7 +1512,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = if not parser.openElements.hasElementInScope(TAG_TABLE): discard else: - while parser.openElements.pop().tagType != TAG_TABLE: discard + while parser.popElement().tagType != TAG_TABLE: discard parser.resetInsertionMode() reprocess token ) @@ -1512,7 +1520,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = if not parser.openElements.hasElementInScope(TAG_TABLE): parse_error else: - while parser.openElements.pop().tagType != TAG_TABLE: discard + while parser.popElement().tagType != TAG_TABLE: discard parser.resetInsertionMode() ) ("</body>", "</caption>", "</col>", "</colgroup>", "</html>", "</tbody>", @@ -1587,7 +1595,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = else: parser.generateImpliedEndTags() if parser.currentNode.tagType != TAG_CAPTION: parse_error - while parser.openElements.pop().tagType != TAG_CAPTION: discard + while parser.popElement().tagType != TAG_CAPTION: discard parser.clearActiveFormattingTillMarker() parser.insertionMode = IN_TABLE ) @@ -1728,7 +1736,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = template close_cell() = parser.generateImpliedEndTags() if parser.currentNode.tagType notin {TAG_TD, TAG_TH}: parse_error - while parser.openElements.pop().tagType notin {TAG_TD, TAG_TH}: discard + while parser.popElement().tagType notin {TAG_TD, TAG_TH}: discard parser.clearActiveFormattingTillMarker() parser.insertionMode = IN_ROW @@ -1739,7 +1747,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = else: parser.generateImpliedEndTags() if parser.currentNode.tagType != token.tagtype: parse_error - while parser.openElements.pop().tagType != token.tagtype: discard + while parser.popElement().tagType != token.tagtype: discard parser.clearActiveFormattingTillMarker() parser.insertionMode = IN_ROW ) @@ -1799,13 +1807,13 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = if not parser.openElements.hasElementInSelectScope(TAG_SELECT): parse_error else: - while parser.openElements.pop().tagType != TAG_SELECT: discard + while parser.popElement().tagType != TAG_SELECT: discard parser.resetInsertionMode() ) "<select>" => (block: parse_error if parser.openElements.hasElementInSelectScope(TAG_SELECT): - while parser.openElements.pop().tagType != TAG_SELECT: discard + while parser.popElement().tagType != TAG_SELECT: discard parser.resetInsertionMode() ) ("<input>", "<keygen>", "<textarea>") => (block: @@ -1813,7 +1821,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = if not parser.openElements.hasElementInSelectScope(TAG_SELECT): discard else: - while parser.openElements.pop().tagType != TAG_SELECT: discard + while parser.popElement().tagType != TAG_SELECT: discard parser.resetInsertionMode() reprocess token ) @@ -1826,7 +1834,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = ("<caption>", "<table>", "<tbody>", "<tfoot>", "<thead>", "<tr>", "<td>", "<th>") => (block: parse_error - while parser.openElements.pop().tagType != TAG_SELECT: discard + while parser.popElement().tagType != TAG_SELECT: discard parser.resetInsertionMode() reprocess token ) @@ -1836,7 +1844,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = if not parser.openElements.hasElementInTableScope(token.tagtype): discard else: - while parser.openElements.pop().tagType != TAG_SELECT: discard + while parser.popElement().tagType != TAG_SELECT: discard parser.resetInsertionMode() reprocess token ) @@ -1887,7 +1895,7 @@ proc processInHTMLContent(parser: var HTML5Parser, token: Token, insertionMode = discard # stop else: parse_error - while parser.openElements.pop().tagType != TAG_TEMPLATE: discard + while parser.popElement().tagType != TAG_TEMPLATE: discard parser.clearActiveFormattingTillMarker() discard parser.templateModes.pop() parser.resetInsertionMode() @@ -1982,7 +1990,7 @@ proc processInForeignContent(parser: var HTML5Parser, token: Token) = for i in countdown(parser.openElements.high, 1): let node = parser.openElements[i] if node.localName == token.tagname: - while parser.openElements.pop() != node: discard + while parser.popElement() != node: discard break if node.namespace == Namespace.HTML: break parser.processInHTMLContent(token) diff --git a/src/html/tags.nim b/src/html/tags.nim index fbec9164..ff6a3a30 100644 --- a/src/html/tags.nim +++ b/src/html/tags.nim @@ -118,7 +118,7 @@ const FormAssociatedElements* = { #TODO support all the other ones const SupportedFormAssociatedElements* = { - TAG_SELECT, TAG_INPUT, TAG_BUTTON + TAG_BUTTON, TAG_INPUT, TAG_SELECT, TAG_TEXTAREA } const ListedElements* = { |