ophinode 0.0.1a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ophinode/nodes/html.py ADDED
@@ -0,0 +1,826 @@
1
+ from .base import *
2
+ from ophinode.exceptions import InvalidAttributeNameError
3
+
4
+ _default_escape_ampersands = False
5
+ _default_escape_tag_delimiters = True
6
+
7
+ def default_escape_ampersands(value: bool = True):
8
+ global _default_escape_ampersands
9
+ _default_escape_ampersands = bool(value)
10
+
11
+ def default_escape_tag_delimiters(value: bool = True):
12
+ global _default_escape_tag_delimiters
13
+ _default_escape_tag_delimiters = bool(value)
14
+
15
+ class HTML5Page(Page):
16
+ @property
17
+ def layout(self):
18
+ return HTML5Layout()
19
+
20
+ def html_attributes(self) -> dict:
21
+ return {}
22
+
23
+ def head(self):
24
+ return []
25
+
26
+ def body(self):
27
+ return []
28
+
29
+ class HTML5Layout(Layout):
30
+ def build(self, page: HTML5Page, context: "ophinode.rendering.RenderContext"):
31
+ return [
32
+ HTML5Doctype(),
33
+ Html(
34
+ Head(
35
+ page.head()
36
+ ),
37
+ Body(
38
+ page.body()
39
+ ),
40
+ **page.html_attributes()
41
+ )
42
+ ]
43
+
44
+ class Node:
45
+ pass
46
+
47
+ class TextNode(Node, ClosedRenderable):
48
+ def __init__(
49
+ self,
50
+ text_content: str,
51
+ *,
52
+ escape_ampersands: bool = None,
53
+ escape_tag_delimiters: bool = None,
54
+ ):
55
+ self._text_content = text_content
56
+ self._escape_ampersands = escape_ampersands
57
+ self._escape_tag_delimiters = escape_tag_delimiters
58
+
59
+ def render(self, context: "ophinode.rendering.RenderContext"):
60
+ text_content = self._text_content
61
+
62
+ escape_ampersands = self._escape_ampersands
63
+ if escape_ampersands is None:
64
+ escape_ampersands = _default_escape_ampersands
65
+ if escape_ampersands:
66
+ text_content = text_content.replace("&", "&")
67
+
68
+ escape_tag_delimiters = self._escape_tag_delimiters
69
+ if escape_tag_delimiters is None:
70
+ escape_tag_delimiters = _default_escape_tag_delimiters
71
+ if escape_tag_delimiters:
72
+ text_content = text_content.replace("<", "&lt;").replace(">", "&gt;")
73
+
74
+ return text_content
75
+
76
+ def escape_ampersands(self, value: bool = True):
77
+ self._escape_ampersands = bool(value)
78
+ return self
79
+
80
+ def escape_tag_delimiters(self, value: bool = True):
81
+ self._escape_tag_delimiters = bool(value)
82
+ return self
83
+
84
+ class HTML5Doctype(Node, ClosedRenderable):
85
+ def render(self, context: "ophinode.rendering.RenderContext"):
86
+ return "<!doctype html>"
87
+
88
+ class CDATASection(Node, OpenRenderable, Expandable, Preparable):
89
+ def __init__(self, *args):
90
+ self._children = list(args)
91
+
92
+ def prepare(self, context: "ophinode.rendering.RenderContext"):
93
+ for c in self._children:
94
+ if isinstance(c, Preparable):
95
+ c.prepare(context)
96
+
97
+ def expand(self, context: "ophinode.rendering.RenderContext"):
98
+ return self._children.copy()
99
+
100
+ def render_start(self, context: "ophinode.rendering.RenderContext"):
101
+ return "<![CDATA[".format(self.tag)
102
+
103
+ def render_end(self, context: "ophinode.rendering.RenderContext"):
104
+ return "]]>".format(self.tag)
105
+
106
+ @property
107
+ def children(self):
108
+ return self._children
109
+
110
+ @property
111
+ def auto_newline(self):
112
+ return False
113
+
114
+ @property
115
+ def auto_indent(self):
116
+ return False
117
+
118
+ class Comment(Node, OpenRenderable, Expandable, Preparable):
119
+ def __init__(self, *args):
120
+ self._children = list(args)
121
+
122
+ def prepare(self, context: "ophinode.rendering.RenderContext"):
123
+ for c in self._children:
124
+ if isinstance(c, Preparable):
125
+ c.prepare(context)
126
+
127
+ def expand(self, context: "ophinode.rendering.RenderContext"):
128
+ return self._children.copy()
129
+
130
+ def render_start(self, context: "ophinode.rendering.RenderContext"):
131
+ return "<!--".format(self.tag)
132
+
133
+ def render_end(self, context: "ophinode.rendering.RenderContext"):
134
+ return "-->".format(self.tag)
135
+
136
+ @property
137
+ def auto_newline(self):
138
+ return False
139
+
140
+ @property
141
+ def auto_indent(self):
142
+ return False
143
+
144
+ class Element(Node):
145
+ def render_attributes(self):
146
+ attribute_order = []
147
+ keys = set(self._attributes)
148
+ for k in ["id", "class", "style", "title"]:
149
+ if k in keys:
150
+ attribute_order.append(k)
151
+ keys.remove(k)
152
+ attribute_order += sorted(keys)
153
+
154
+ rendered = []
155
+ for k in attribute_order:
156
+ for c in k:
157
+ if c in " \"'>/=":
158
+ raise InvalidAttributeNameError(k)
159
+ v = self._attributes[k]
160
+ if isinstance(v, bool):
161
+ if v:
162
+ rendered.append("{}".format(k))
163
+ else:
164
+ escaped = str(v)
165
+
166
+ escape_ampersands = self._escape_ampersands
167
+ if escape_ampersands is None:
168
+ escape_ampersands = _default_escape_ampersands
169
+ if escape_ampersands:
170
+ escaped = escaped.replace("&", "&amp;")
171
+
172
+ escape_tag_delimiters = self._escape_tag_delimiters
173
+ if escape_tag_delimiters is None:
174
+ escape_tag_delimiters = _default_escape_tag_delimiters
175
+ if escape_tag_delimiters:
176
+ escaped = escaped.replace("<", "&lt;").replace(">", "&gt;")
177
+
178
+ escaped = escaped.replace("\"", "&quot;")
179
+ rendered.append("{}=\"{}\"".format(k, escaped))
180
+
181
+ return " ".join(rendered)
182
+
183
+ @property
184
+ def attributes(self):
185
+ return self._attributes
186
+
187
+ def escape_ampersands(self, value: bool = True):
188
+ self._escape_ampersands = bool(value)
189
+ return self
190
+
191
+ def escape_tag_delimiters(self, value: bool = True):
192
+ self._escape_tag_delimiters = bool(value)
193
+ return self
194
+
195
+ class OpenElement(Element, OpenRenderable, Expandable, Preparable):
196
+ tag = "div"
197
+ render_mode = "hierarchy"
198
+
199
+ def __init__(
200
+ self,
201
+ *args,
202
+ class_name = None,
203
+ html_for = None,
204
+ html_as = None,
205
+ html_async = None,
206
+ accept_charset = None,
207
+ escape_ampersands = None,
208
+ escape_tag_delimiters = None,
209
+ children = None,
210
+ **kwargs
211
+ ):
212
+ self._children = list(args)
213
+ if children is not None:
214
+ for c in children:
215
+ self._children.append(c)
216
+ self._attributes = dict(kwargs)
217
+ if class_name is not None:
218
+ self._attributes["class"] = class_name
219
+ if html_for is not None:
220
+ self._attributes["for"] = html_for
221
+ if html_as is not None:
222
+ self._attributes["as"] = html_as
223
+ if html_async is not None:
224
+ self._attributes["async"] = html_async
225
+ if accept_charset is not None:
226
+ self._attributes["accept-charset"] = accept_charset
227
+ self._escape_ampersands = escape_ampersands
228
+ self._escape_tag_delimiters = escape_tag_delimiters
229
+
230
+ def prepare(self, context: "ophinode.rendering.RenderContext"):
231
+ for c in self._children:
232
+ if isinstance(c, Preparable):
233
+ c.prepare(context)
234
+
235
+ def expand(self, context: "ophinode.rendering.RenderContext"):
236
+ expansion = []
237
+ for c in self._children:
238
+ if isinstance(c, str):
239
+ node = TextNode(c)
240
+ if self._escape_ampersands is not None:
241
+ node.escape_ampersands(self._escape_ampersands)
242
+ if self._escape_tag_delimiters is not None:
243
+ node.escape_tag_delimiters(self._escape_tag_delimiters)
244
+ expansion.append(node)
245
+ else:
246
+ expansion.append(c)
247
+ return expansion
248
+
249
+ def render_start(self, context: "ophinode.rendering.RenderContext"):
250
+ rendered_attributes = self.render_attributes()
251
+ if rendered_attributes:
252
+ return "<{} {}>".format(self.tag, rendered_attributes)
253
+ return "<{}>".format(self.tag)
254
+
255
+ def render_end(self, context: "ophinode.rendering.RenderContext"):
256
+ return "</{}>".format(self.tag)
257
+
258
+ @property
259
+ def children(self):
260
+ return self._children
261
+
262
+ @property
263
+ def auto_newline(self):
264
+ return self.render_mode == "hierarchy"
265
+
266
+ @property
267
+ def auto_indent(self):
268
+ return self.render_mode == "phrase" or self.render_mode == "hierarchy"
269
+
270
+ class ClosedElement(Element, ClosedRenderable):
271
+ tag = "meta"
272
+
273
+ def __init__(
274
+ self,
275
+ *,
276
+ class_name = None,
277
+ html_for = None,
278
+ html_as = None,
279
+ html_async = None,
280
+ accept_charset = None,
281
+ escape_ampersands = None,
282
+ escape_tag_delimiters = None,
283
+ **kwargs
284
+ ):
285
+ self._attributes = dict(kwargs)
286
+ if class_name is not None:
287
+ self._attributes["class"] = class_name
288
+ if html_for is not None:
289
+ self._attributes["for"] = html_for
290
+ if html_as is not None:
291
+ self._attributes["as"] = html_as
292
+ if html_async is not None:
293
+ self._attributes["async"] = html_async
294
+ if accept_charset is not None:
295
+ self._attributes["accept-charset"] = accept_charset
296
+ self._escape_ampersands = escape_ampersands
297
+ self._escape_tag_delimiters = escape_tag_delimiters
298
+
299
+ def render(self, context: "ophinode.rendering.RenderContext"):
300
+ rendered_attributes = self.render_attributes()
301
+ if rendered_attributes:
302
+ return "<{} {}>".format(self.tag, rendered_attributes)
303
+ return "<{}>".format(self.tag)
304
+
305
+ # --- The document element ---
306
+
307
+ class HtmlElement(OpenElement):
308
+ tag = "html"
309
+
310
+ Html = HtmlElement
311
+
312
+ # --- Document metadata ---
313
+
314
+ class HeadElement(OpenElement):
315
+ tag = "head"
316
+
317
+ class TitleElement(OpenElement):
318
+ tag = "title"
319
+ render_mode = "inline"
320
+
321
+ class BaseElement(ClosedElement):
322
+ tag = "base"
323
+
324
+ class LinkElement(ClosedElement):
325
+ tag = "link"
326
+
327
+ class MetaElement(ClosedElement):
328
+ tag = "meta"
329
+
330
+ class StyleElement(OpenElement):
331
+ tag = "style"
332
+
333
+ Head = HeadElement
334
+ Title = TitleElement
335
+ Base = BaseElement
336
+ Link = LinkElement
337
+ Meta = MetaElement
338
+ Style = StyleElement
339
+
340
+ # --- Sections ---
341
+
342
+ class BodyElement(OpenElement):
343
+ tag = "body"
344
+
345
+ class ArticleElement(OpenElement):
346
+ tag = "article"
347
+
348
+ class SectionElement(OpenElement):
349
+ tag = "section"
350
+
351
+ class NavigationElement(OpenElement):
352
+ tag = "nav"
353
+
354
+ class AsideElement(OpenElement):
355
+ tag = "aside"
356
+
357
+ class HeadingLevel1Element(OpenElement):
358
+ tag = "h1"
359
+ render_mode = "inline"
360
+
361
+ class HeadingLevel2Element(OpenElement):
362
+ tag = "h2"
363
+ render_mode = "inline"
364
+
365
+ class HeadingLevel3Element(OpenElement):
366
+ tag = "h3"
367
+ render_mode = "inline"
368
+
369
+ class HeadingLevel4Element(OpenElement):
370
+ tag = "h4"
371
+ render_mode = "inline"
372
+
373
+ class HeadingLevel5Element(OpenElement):
374
+ tag = "h5"
375
+ render_mode = "inline"
376
+
377
+ class HeadingLevel6Element(OpenElement):
378
+ tag = "h6"
379
+ render_mode = "inline"
380
+
381
+ class HeadingGroupElement(OpenElement):
382
+ tag = "hgroup"
383
+
384
+ class HeaderElement(OpenElement):
385
+ tag = "header"
386
+
387
+ class FooterElement(OpenElement):
388
+ tag = "footer"
389
+
390
+ class AddressElement(OpenElement):
391
+ tag = "address"
392
+
393
+ Body = BodyElement
394
+ Article = ArticleElement
395
+ Section = SectionElement
396
+ Nav = NavigationElement
397
+ Aside = AsideElement
398
+ H1 = HeadingLevel1Element
399
+ H2 = HeadingLevel2Element
400
+ H3 = HeadingLevel3Element
401
+ H4 = HeadingLevel4Element
402
+ H5 = HeadingLevel5Element
403
+ H6 = HeadingLevel6Element
404
+ HGroup = HeadingGroupElement
405
+ Header = HeaderElement
406
+ Footer = FooterElement
407
+ Address = AddressElement
408
+
409
+ # --- Grouping content ---
410
+
411
+ class ParagraphElement(OpenElement):
412
+ tag = "p"
413
+ render_mode = "phrase"
414
+
415
+ class HorizontalRuleElement(ClosedElement):
416
+ tag = "hr"
417
+
418
+ class PreformattedTextElement(OpenElement):
419
+ tag = "pre"
420
+ render_mode = "pre"
421
+
422
+ class BlockQuotationElement(OpenElement):
423
+ tag = "blockquote"
424
+
425
+ class OrderedListElement(OpenElement):
426
+ tag = "ol"
427
+
428
+ class UnorderedListElement(OpenElement):
429
+ tag = "ul"
430
+
431
+ class MenuElement(OpenElement):
432
+ tag = "menu"
433
+
434
+ class ListItemElement(OpenElement):
435
+ tag = "li"
436
+
437
+ class DescriptionListElement(OpenElement):
438
+ tag = "dl"
439
+
440
+ class DescriptionTermElement(OpenElement):
441
+ tag = "dt"
442
+ render_mode = "inline"
443
+
444
+ class DescriptionDetailsElement(OpenElement):
445
+ tag = "dd"
446
+ render_mode = "inline"
447
+
448
+ class FigureElement(OpenElement):
449
+ tag = "figure"
450
+
451
+ class FigureCaptionElement(OpenElement):
452
+ tag = "figcaption"
453
+
454
+ class MainElement(OpenElement):
455
+ tag = "main"
456
+
457
+ class SearchElement(OpenElement):
458
+ tag = "search"
459
+
460
+ class DivisionElement(OpenElement):
461
+ tag = "div"
462
+
463
+ P = ParagraphElement
464
+ HR = HorizontalRuleElement
465
+ Pre = PreformattedTextElement
466
+ BlockQuote = BlockQuotationElement
467
+ OL = OrderedListElement
468
+ UL = UnorderedListElement
469
+ Menu = MenuElement
470
+ LI = ListItemElement
471
+ DL = DescriptionListElement
472
+ DT = DescriptionTermElement
473
+ DD = DescriptionDetailsElement
474
+ Figure = FigureElement
475
+ FigCaption = FigureCaptionElement
476
+ Main = MainElement
477
+ Search = SearchElement
478
+ Div = DivisionElement
479
+
480
+ # --- Text-level semantics ---
481
+
482
+ class AnchorElement(OpenElement):
483
+ tag = "a"
484
+
485
+ class EmphasisElement(OpenElement):
486
+ tag = "em"
487
+ render_mode = "inline"
488
+
489
+ class StrongImportanceElement(OpenElement):
490
+ tag = "strong"
491
+ render_mode = "inline"
492
+
493
+ class SmallPrintElement(OpenElement):
494
+ tag = "small"
495
+ render_mode = "inline"
496
+
497
+ class StrikethroughElement(OpenElement):
498
+ tag = "s"
499
+ render_mode = "inline"
500
+
501
+ class CitationElement(OpenElement):
502
+ tag = "cite"
503
+ render_mode = "inline"
504
+
505
+ class QuotationElement(OpenElement):
506
+ tag = "q"
507
+ render_mode = "inline"
508
+
509
+ class DefinitionElement(OpenElement):
510
+ tag = "dfn"
511
+ render_mode = "inline"
512
+
513
+ class AbbreviationElement(OpenElement):
514
+ tag = "abbr"
515
+ render_mode = "inline"
516
+
517
+ class RubyAnnotationElement(OpenElement):
518
+ tag = "ruby"
519
+ render_mode = "inline"
520
+
521
+ class RubyTextElement(OpenElement):
522
+ tag = "rt"
523
+ render_mode = "inline"
524
+
525
+ class RubyParenthesesElement(OpenElement):
526
+ tag = "rp"
527
+ render_mode = "inline"
528
+
529
+ class DataElement(OpenElement):
530
+ tag = "data"
531
+ render_mode = "inline"
532
+
533
+ class TimeElement(OpenElement):
534
+ tag = "time"
535
+ render_mode = "inline"
536
+
537
+ class CodeElement(OpenElement):
538
+ tag = "code"
539
+ render_mode = "inline"
540
+
541
+ class VariableElement(OpenElement):
542
+ tag = "var"
543
+ render_mode = "inline"
544
+
545
+ class SampleElement(OpenElement):
546
+ tag = "samp"
547
+ render_mode = "inline"
548
+
549
+ class KeyboardInputElement(OpenElement):
550
+ tag = "kbd"
551
+ render_mode = "inline"
552
+
553
+ class SubscriptElement(OpenElement):
554
+ tag = "sub"
555
+ render_mode = "inline"
556
+
557
+ class SuperscriptElement(OpenElement):
558
+ tag = "sup"
559
+ render_mode = "inline"
560
+
561
+ class ItalicTextElement(OpenElement):
562
+ tag = "i"
563
+ render_mode = "inline"
564
+
565
+ class BoldTextElement(OpenElement):
566
+ tag = "b"
567
+ render_mode = "inline"
568
+
569
+ class UnarticulatedAnnotationElement(OpenElement):
570
+ tag = "u"
571
+ render_mode = "inline"
572
+
573
+ class MarkedTextElement(OpenElement):
574
+ tag = "mark"
575
+ render_mode = "inline"
576
+
577
+ class BidirectionalIsolateElement(OpenElement):
578
+ tag = "bdi"
579
+ render_mode = "inline"
580
+
581
+ class BidirectionalOverrideElement(OpenElement):
582
+ tag = "bdo"
583
+ render_mode = "inline"
584
+
585
+ class SpanElement(OpenElement):
586
+ tag = "span"
587
+ render_mode = "inline"
588
+
589
+ class LineBreakElement(ClosedElement):
590
+ tag = "br"
591
+
592
+ class LineBreakOpportunityElement(ClosedElement):
593
+ tag = "wbr"
594
+
595
+ A = AnchorElement
596
+ EM = EmphasisElement
597
+ Strong = StrongImportanceElement
598
+ Small = SmallPrintElement
599
+ S = StrikethroughElement
600
+ Cite = CitationElement
601
+ Q = QuotationElement
602
+ Dfn = DefinitionElement
603
+ Abbr = AbbreviationElement
604
+ Ruby = RubyAnnotationElement
605
+ RT = RubyTextElement
606
+ RP = RubyParenthesesElement
607
+ Data = DataElement
608
+ Time = TimeElement
609
+ Code = CodeElement
610
+ Var = VariableElement
611
+ Samp = SampleElement
612
+ Kbd = KeyboardInputElement
613
+ Sub = SubscriptElement
614
+ Sup = SuperscriptElement
615
+ I = ItalicTextElement
616
+ B = BoldTextElement
617
+ U = UnarticulatedAnnotationElement
618
+ Mark = MarkedTextElement
619
+ BDI = BidirectionalIsolateElement
620
+ BDO = BidirectionalOverrideElement
621
+ Span = SpanElement
622
+ BR = LineBreakElement
623
+ WBR = LineBreakOpportunityElement
624
+
625
+ # --- Edits ---
626
+
627
+ class InsertionElement(OpenElement):
628
+ tag = "ins"
629
+
630
+ class DeletionElement(OpenElement):
631
+ tag = "del"
632
+ render_mode = "inline"
633
+
634
+ Ins = InsertionElement
635
+ Del = DeletionElement
636
+
637
+ # --- Embedded content ---
638
+
639
+ class PictureElement(OpenElement):
640
+ tag = "picture"
641
+
642
+ class SourceElement(ClosedElement):
643
+ tag = "source"
644
+
645
+ class ImageElement(ClosedElement):
646
+ tag = "image"
647
+
648
+ class InlineFrameElement(OpenElement):
649
+ tag = "iframe"
650
+
651
+ class EmbeddedContentElement(ClosedElement):
652
+ tag = "embed"
653
+
654
+ class ExternalObjectElement(OpenElement):
655
+ tag = "object"
656
+
657
+ class VideoElement(OpenElement):
658
+ tag = "video"
659
+
660
+ class AudioElement(OpenElement):
661
+ tag = "audio"
662
+
663
+ class TextTrackElement(ClosedElement):
664
+ tag = "track"
665
+
666
+ class ImageMapElement(OpenElement):
667
+ tag = "map"
668
+
669
+ class ImageMapAreaElement(ClosedElement):
670
+ tag = "area"
671
+
672
+ Picture = PictureElement
673
+ Source = SourceElement
674
+ Image = ImageElement
675
+ IFrame = InlineFrameElement
676
+ Embed = EmbeddedContentElement
677
+ Object = ExternalObjectElement
678
+ Video = VideoElement
679
+ Audio = AudioElement
680
+ Track = TextTrackElement
681
+ Map = ImageMapElement
682
+ Area = ImageMapAreaElement
683
+
684
+ # --- Tabular data ---
685
+
686
+ class TableElement(OpenElement):
687
+ tag = "table"
688
+
689
+ class TableCaptionElement(OpenElement):
690
+ tag = "caption"
691
+
692
+ class TableColumnGroupElement(OpenElement):
693
+ tag = "colgroup"
694
+
695
+ class TableColumnElement(ClosedElement):
696
+ tag = "col"
697
+
698
+ class TableBodyElement(OpenElement):
699
+ tag = "tbody"
700
+
701
+ class TableHeadElement(OpenElement):
702
+ tag = "thead"
703
+
704
+ class TableFootElement(OpenElement):
705
+ tag = "tfoot"
706
+
707
+ class TableRowElement(OpenElement):
708
+ tag = "tr"
709
+
710
+ class TableDataCellElement(OpenElement):
711
+ tag = "td"
712
+
713
+ class TableHeaderCellElement(OpenElement):
714
+ tag = "th"
715
+
716
+ Table = TableElement
717
+ Caption = TableCaptionElement
718
+ ColGroup = TableColumnGroupElement
719
+ Column = TableColumnElement
720
+ TBody = TableBodyElement
721
+ THead = TableHeadElement
722
+ TFoot = TableFootElement
723
+ TR = TableRowElement
724
+ TD = TableDataCellElement
725
+ TH = TableHeaderCellElement
726
+
727
+ # --- Forms ---
728
+
729
+ class FormElement(OpenElement):
730
+ tag = "form"
731
+
732
+ class LabelElement(OpenElement):
733
+ tag = "label"
734
+
735
+ class InputElement(ClosedElement):
736
+ tag = "input"
737
+
738
+ class ButtonElement(OpenElement):
739
+ tag = "button"
740
+
741
+ class SelectElement(OpenElement):
742
+ tag = "select"
743
+
744
+ class DataListElement(OpenElement):
745
+ tag = "datalist"
746
+
747
+ class OptionGroupElement(OpenElement):
748
+ tag = "optgroup"
749
+
750
+ class OptionElement(OpenElement):
751
+ tag = "option"
752
+
753
+ class TextAreaElement(OpenElement):
754
+ tag = "textarea"
755
+ render_mode = "pre"
756
+
757
+ class OutputElement(OpenElement):
758
+ tag = "output"
759
+
760
+ class ProgressElement(OpenElement):
761
+ tag = "progress"
762
+ render_mode = "inline"
763
+
764
+ class MeterElement(OpenElement):
765
+ tag = "meter"
766
+ render_mode = "inline"
767
+
768
+ class FieldSetElement(OpenElement):
769
+ tag = "fieldset"
770
+
771
+ class FieldSetLegendElement(OpenElement):
772
+ tag = "legend"
773
+ render_mode = "inline"
774
+
775
+ Form = FormElement
776
+ Label = LabelElement
777
+ Input = InputElement
778
+ Button = ButtonElement
779
+ Select = SelectElement
780
+ DataList = DataListElement
781
+ OptGroup = OptionGroupElement
782
+ Option = OptionElement
783
+ Progress = ProgressElement
784
+ Meter = MeterElement
785
+ FieldSet = FieldSetElement
786
+ Legend = FieldSetLegendElement
787
+
788
+ # --- Interactive elements ---
789
+
790
+ class DetailsElement(OpenElement):
791
+ tag = "details"
792
+
793
+ class SummaryElement(OpenElement):
794
+ tag = "summary"
795
+ render_mode = "inline"
796
+
797
+ class DialogElement(OpenElement):
798
+ tag = "dialog"
799
+
800
+ Details = DetailsElement
801
+ Summary = SummaryElement
802
+ Dialog = DialogElement
803
+
804
+ # --- Scripting ---
805
+
806
+ class ScriptElement(OpenElement):
807
+ tag = "script"
808
+
809
+ class NoScriptElement(OpenElement):
810
+ tag = "noscript"
811
+
812
+ class TemplateElement(OpenElement):
813
+ tag = "template"
814
+
815
+ class SlotElement(OpenElement):
816
+ tag = "slot"
817
+
818
+ class CanvasElement(OpenElement):
819
+ tag = "canvas"
820
+
821
+ Script = ScriptElement
822
+ NoScript = NoScriptElement
823
+ Template = TemplateElement
824
+ Slot = SlotElement
825
+ Canvas = CanvasElement
826
+