1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132(* Copyright 2024 Yawar Amin
This file is part of dream-html.
dream-html is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
dream-html 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 General Public License for more
details.
You should have received a copy of the GNU General Public License along with
dream-html. If not, see <https://www.gnu.org/licenses/>. *)typeattr=string*stringtypetag={name:string;attrs:attrlist;children:nodelistoption}andnode=|Tagoftag|Txtofstring|Commentofstringletis_txt=function|Txt_->true|_->falseletis_null=function|Tag{name="";_}->true|_->falseletrecfold~tag~txt~commentvalue=function|Tag{name;attrs;children=None}->tagnameattrsvalue|Tag{name;attrs;children=Somec}->c|>List.fold_left(fold~tag~txt~comment)value|>tagnameattrs|Txtt->txttvalue|Commentc->commentcvaluetype'ato_attr='a->attrtype'astring_attr=('a,unit,string,attr)format4->'atypestd_tag=attrlist->nodelist->nodetypevoid_tag=attrlist->nodetype'atext_tag=attrlist->('a,unit,string,node)format4->'aletwrite_attr~xmlp=function|"",_->()|name,""whennotxml->p" ";pname|name,value->p" ";pname;p{|="|};pvalue;p{|"|}letxml_modexmlname=matchxml,namewith|true,_|_,("math"|"svg"|"rss")->true|_->falsemoduleIndent_level=structtypet=intoptionletnext(t:t)=Option.mapsucct(* The following functions return string that should be inserted before or
after the open or closing tag with indent level t. Some of them also need to
know if the next tag is going to be indented one more level. *)letopen_prefix(t:t)=matchtwith|None->""|Somen->String.make(n*2)' 'letopen_suffix(t:t)~indent_next=matcht,indent_nextwith|Some_,true->"\n"|_,_->""letclose_prefix(t:t)~indent_next=matcht,indent_nextwith|Somen,true->String.make(n*2)' '|_,_->""letclose_suffix(t:t)=matchtwith|None->""|Some_->"\n"endletshould_indent_nextchildren=matchchildrenwith|[]->false|children->not(List.existsis_txtchildren)(* Loosely based on https://www.w3.org/TR/DOM-Parsing/. Pretty prints using two
spaces for indentation. On high level, the algorithm indents the children of a
tag if they do not contain a txt node. No newline is inserted if there are no
children nodes. *)letrecwrite_tag~indent_level~xmlpnode=ifnot(is_nullnode)thenp(Indent_level.open_prefixindent_level);(matchnodewith|Tag{name="";children=Somechildren;_}->List.iter(write_tag~indent_level:(ifshould_indent_nextchildrenthenindent_levelelseNone)~xmlp)children|Tag{name;attrs;children=Some[]}whenxml->p"<";pname;List.iter(write_attr~xmlp)attrs;p" />"|Tag{name;attrs;children=Somechildren}->letxml=xml_modexmlnameinletindent_next=should_indent_nextchildreninifname="html"then(p"<!DOCTYPE html>";p(Indent_level.open_suffixindent_level~indent_next);p(Indent_level.open_prefixindent_level));p"<";pname;List.iter(write_attr~xmlp)attrs;p">";p(Indent_level.open_suffixindent_level~indent_next);List.iter(write_tag~indent_level:(ifindent_nextthenIndent_level.nextindent_levelelseNone)~xmlp)children;p(Indent_level.close_prefixindent_level~indent_next);p"</";pname;p">"|Tag{name;attrs;children=None}->letxml=xml_modexmlnameinp"<";pname;List.iter(write_attr~xmlp)attrs;p(ifxmlthen" />"else">")|Txtstr->pstr|Commentstr->p"<!-- ";pstr;p" -->");ifnot(is_nullnode)thenp(Indent_level.close_suffixindent_level)letto_string~xmlnode=letbuf=Buffer.create256inwrite_tag~indent_level:(Some0)~xml(Buffer.add_stringbuf)node;Buffer.contentsbufletppppfnode=node|>to_string~xml:false|>Format.pp_print_stringppfletxml_header={|<?xml version="1.0" encoding="UTF-8"?>
|}letto_xml?(header=false)node=(ifheaderthenxml_headerelse"")^to_string~xml:truenodeletto_string=to_string~xml:falseletpp_xmlppf?headernode=node|>to_xml?header|>Format.pp_print_stringppflettxt_escapebuffer=function|'&'->Buffer.add_stringbuffer"&"|'<'->Buffer.add_stringbuffer"<"|'>'->Buffer.add_stringbuffer">"|c->Buffer.add_charbufferclettxt_escaperaws=ifrawthenselseletbuffer=Buffer.create(String.lengths*2)inString.iter(txt_escapebuffer)s;Buffer.contentsbufferletattr_escapebuffer=function|'&'->Buffer.add_stringbuffer"&"|'"'->Buffer.add_stringbuffer"""|'<'->Buffer.add_stringbuffer"<"|'>'->Buffer.add_stringbuffer">"|c->Buffer.add_charbuffercletattr_escaperaws=ifrawthenselseletbuffer=Buffer.create(String.lengths*2)inString.iter(attr_escapebuffer)s;Buffer.contentsbufferletattrname=name,""(* Helper to encode attribute values which contain double-quote characters and
therefore MUST be quoted with single-quotes. *)letsqattrnamefmt=Printf.ksprintf(funs->name^"='"^s^"'","")fmtletstring_attrname?(raw=false)fmt=Printf.ksprintf(funs->name,attr_escaperaws)fmtleturi_escapes=s|>Uri.of_string|>Uri.to_string|>attr_escapefalseleturi_attrnamefmt=Printf.ksprintf(funs->name,uri_escapes)fmtletbool_attrnamevalue=name,string_of_boolvalueletfloat_attrnamevalue=name,Printf.sprintf"%f"valueletint_attrnamevalue=name,string_of_intvalueletstd_tagnameattrschildren=Tag{name;attrs;children=Somechildren}letvoid_tagnameattrs=Tag{name;attrs;children=None}lettext_tagname?(raw=false)attrsfmt=Printf.ksprintf(funs->Tag{name;attrs;children=Some[Txt(txt_escaperaws)]})fmtleturi_tagnameattrsfmt=Printf.ksprintf(funs->Tag{name;attrs;children=Some[Txt(uri_escapes)]})fmtlettxt?(raw=false)fmt=Printf.ksprintf(funs->Txt(txt_escaperaws))fmtletcommentstr=Comment(txt_escapefalsestr)let(+@)node((name,value)asattr)=matchnodewith|Tagt->(matchList.find_opt(fun(n,_)->n=name)t.attrs,namewith|Some(_,classes),"class"->letnew_attr=string_attr"class""%s %s"classesvalueinTag{twithattrs=List.map(function|"class",_->new_attr|attr->attr)t.attrs}|Some_,_->Printf.ksprintfinvalid_arg"duplicate attribute: %s"name|None,_->Tag{twithattrs=attr::t.attrs})|_->invalid_arg"cannot add attribute to non-tag node"let(-@)nodeattr=matchnodewith|Tagt->Tag{twithattrs=List.filter(fun(k,_)->k<>attr)t.attrs}|_->invalid_arg"cannot remove attribute from non-tag node"let(.@[])nodeattr=matchnodewith|Tag{attrs;_}->List.assocattrattrs|_->invalid_arg"cannot get value of attribute from non-tag node"letis_null_(name,_)=name=""moduleHTML=struct(* Attributes *)typemethod_=[`GET|`POST|`dialog]typeenctype=[`urlencoded|`formdata|`text_plain]letenctype_string=function|`urlencoded->"application/x-www-form-urlencoded"|`formdata->"multipart/form-data"|`text_plain->"text/plain"letnull_=string_attr""""letacceptfmt=string_attr"accept"fmtletaccept_charsetfmt=string_attr"accept-charset"fmtletaccesskeyfmt=string_attr"accesskey"fmtletactionfmt=uri_attr"action"fmtletalignfmt=string_attr"align"fmtletallowfmt=string_attr"allow"fmtletaltfmt=string_attr"alt"fmtletas_fmt=string_attr"as"fmtletasync=attr"async"letautocapitalizevalue=("autocapitalize",matchvaluewith|`off->"off"|`none->"none"|`on->"on"|`sentences->"sentences"|`words->"words"|`characters->"characters")letautocompletevalue=("autocomplete",matchvaluewith|`off->"off"|`on->"on"|`name->"name"|`honorific_prefix->"honorific-prefix"|`given_name->"given-name"|`additional_name->"additional-name"|`honorific_suffix->"honorific-suffix"|`nickname->"nickname"|`email->"email"|`username->"username"|`new_password->"new-password"|`current_password->"current-password"|`one_time_code->"one-time-code"|`organization_title->"organization-title"|`organization->"organization"|`street_address->"street-address"|`address_line1->"address-line1"|`address_line2->"address-line2"|`address_line3->"address-line3"|`address_level4->"address-level4"|`address_level3->"address-level3"|`address_level2->"address-level2"|`address_level1->"address-level1"|`country->"country"|`country_name->"country-name"|`postal_code->"postal-code"|`cc_name->"cc-name"|`cc_given_name->"cc-given-name"|`cc_additional_name->"cc-additional-name"|`cc_family_name->"cc-family-name"|`cc_number->"cc-number"|`cc_exp->"cc-exp"|`cc_exp_month->"cc-exp-month"|`cc_exp_year->"cc-exp-year"|`cc_csc->"cc-csc"|`cc_type->"cc-type"|`transaction_currency->"transaction-currency"|`transaction_amount->"transaction-amount"|`language->"language"|`bday->"bday"|`bday_day->"bday-day"|`bday_month->"bday-month"|`bday_year->"bday-year"|`sex->"sex"|`tel->"tel"|`tel_country_code->"tel-country-code"|`tel_national->"tel-national"|`tel_area_code->"tel-area-code"|`tel_local->"tel-local"|`tel_extension->"tel-extension"|`impp->"impp"|`url->"url"|`photo->"photo"|`webauthn->"webauthn")letautofocus=attr"autofocus"letautoplay=attr"autoplay"letbufferedfmt=string_attr"buffered"fmtletcapturevalue=("capture",matchvaluewith|`user->"user"|`environment->"environment")letcharsetfmt=string_attr"charset"fmtletchecked=attr"checked"letcite_fmt=uri_attr"cite"fmtletclass_fmt=string_attr"class"fmtletcolorfmt=string_attr"color"fmtletcols=int_attr"cols"letcolspan=int_attr"colspan"letcontentfmt=string_attr"content"fmtletcontenteditablevalue=("contenteditable",matchvaluewith|`true_->"true"|`false_->"false"|`plaintext_only->"plaintext-only")letcontextmenufmt=string_attr"contextmenu"fmtletcontrols=attr"controls"letcoordsfmt=string_attr"coords"fmtletcrossoriginvalue=("crossorigin",matchvaluewith|`anonymous->"anonymous"|`use_credentials->"use-credentials")letdata_fmt=uri_attr"data"fmtletdatetimefmt=string_attr"datetime"fmtletdecodingvalue=("decoding",matchvaluewith|`sync->"sync"|`async->"async"|`auto->"auto")letdefault=attr"default"letdefer=attr"defer"letdirvalue=("dir",matchvaluewith|`ltr->"ltr"|`rtl->"rtl"|`auto->"auto")letdirnamefmt=string_attr"dirname"fmtletdisabled=attr"disabled"letdownloadfmt=string_attr"download"fmtletdraggable=attr"draggable"letenctypevalue="enctype",enctype_stringvalueletfetchpriorityvalue=("fetchpriority",matchvaluewith|`high->"high"|`low->"low"|`auto->"auto")letfor_fmt=string_attr"for"fmtletform_fmt=string_attr"form"fmtletformactionfmt=string_attr"formaction"fmtletformenctypevalue="formenctype",enctype_stringvalueletmethod_to_string=function|`GET->"get"|`POST->"post"|`dialog->"dialog"letformmethodvalue="formmethod",method_to_stringvalueletformnovalidate=attr"formnovalidate"letformtargetfmt=string_attr"formtarget"fmtletheadersfmt=string_attr"headers"fmtletheightfmt=string_attr"height"fmtlethiddenvalue=("hidden",matchvaluewith|`hidden->"hidden"|`until_found->"until-found")lethigh=float_attr"high"lethreffmt=uri_attr"href"fmtlethreflangfmt=string_attr"hreflang"fmtlethttp_equivvalue=("http-equiv",matchvaluewith|`content_security_policy->"content-security-policy"|`content_type->"content-type"|`default_style->"default-style"|`x_ua_compatible->"x-ua-compatible"|`refresh->"refresh")letidfmt=string_attr"id"fmtletinert=attr"inert"letintegrityfmt=string_attr"integrity"fmtletinputmodevalue=("inputmode",matchvaluewith|`none->"none"|`text->"text"|`decimal->"decimal"|`numeric->"numeric"|`tel->"tel"|`search->"search"|`email->"email"|`url->"url")letismap=attr"ismap"letitempropfmt=string_attr"itemprop"fmtletkindvalue=("kind",matchvaluewith|`subtitles->"subtitles"|`captions->"captions"|`descriptions->"descriptions"|`chapters->"chapters"|`metadata->"metadata")letlabel_fmt=string_attr"label"fmtletlangfmt=string_attr"lang"fmtletlistfmt=string_attr"list"fmtletloading_lazy=string_attr"loading""lazy"letloop=attr"loop"letlow=float_attr"low"letmaxfmt=string_attr"max"fmtletmaxlength=int_attr"maxlength"letmediafmt=string_attr"media"fmtletmethod_value="method",method_to_stringvalueletminfmt=string_attr"min"fmtletminlength=int_attr"minlength"letmultiple=attr"multiple"letmuted=attr"muted"letnamefmt=string_attr"name"fmtletnovalidate=attr"novalidate"letonblurfmt=string_attr"onblur"~raw:truefmtletonclickfmt=string_attr"onclick"~raw:truefmtletopen_=attr"open"letoptimum=float_attr"optimum"letpatternfmt=string_attr"pattern"fmtletpingfmt=string_attr"ping"fmtletplaceholderfmt=string_attr"placeholder"fmtletplaysinline=attr"playsinline"letpopovervalue=("popover",matchvaluewith|`auto->"auto"|`manual->"manual")letpopovertargetfmt=string_attr"popovertarget"fmtletpopovertargetactionvalue=("popovertargetaction",matchvaluewith|`hide->"hide"|`show->"show")letposterfmt=uri_attr"poster"fmtletpreloadvalue=("preload",matchvaluewith|`none->"none"|`metadata->"metadata"|`auto->"auto")letreadonly=attr"readonly"letreferrerpolicyvalue=("referrerpolicy",matchvaluewith|`no_referrer->"no-referrer"|`no_referrer_when_downgrade->"no-referrer-when-downgrade"|`origin->"origin"|`origin_when_cross_origin->"origin-when-cross-origin"|`same_origin->"same-origin"|`strict_origin->"strict-origin"|`strict_origin_when_cross_origin->"strict-origin-when-cross-origin"|`unsafe_url->"unsafe-url")letrelfmt=string_attr"rel"fmtletrequired=attr"required"letreversed=attr"reversed"letrolevalue=("role",matchvaluewith|`alert->"alert"|`alertdialog->"alertdialog"|`application->"application"|`article->"article"|`banner->"banner"|`button->"button"|`cell->"cell"|`checkbox->"checkbox"|`columnheader->"columnheader"|`combobox->"combobox"|`comment->"comment"|`complementary->"complementary"|`contentinfo->"contentinfo"|`definition->"definition"|`dialog->"dialog"|`document->"document"|`feed->"feed"|`figure->"figure"|`form->"form"|`generic->"generic"|`grid->"grid"|`gridcell->"gridcell"|`group->"group"|`heading->"heading"|`img->"img"|`link->"link"|`list->"list"|`listbox->"listbox"|`listitem->"listitem"|`log->"log"|`main->"main"|`mark->"mark"|`marquee->"marquee"|`math->"math"|`menu->"menu"|`menubar->"menubar"|`menuitem->"menuitem"|`menuitemcheckbox->"menuitemcheckbox"|`menuitemradio->"menuitemradio"|`meter->"meter"|`navigation->"navigation"|`none->"none"|`note->"note"|`option->"option"|`presentation->"presentation"|`progressbar->"progressbar"|`radio->"radio"|`radiogroup->"radiogroup"|`region->"region"|`row->"row"|`rowgroup->"rowgroup"|`rowheader->"rowheader"|`scrollbar->"scrollbar"|`search->"search"|`searchbox->"searchbox"|`separator->"separator"|`slider->"slider"|`spinbutton->"spinbutton"|`status->"status"|`suggestion->"suggestion"|`switch->"switch"|`tab->"tab"|`table->"table"|`tablist->"tablist"|`tabpanel->"tabpanel"|`term->"term"|`textbox->"textbox"|`timer->"timer"|`toolbar->"toolbar"|`tooltip->"tooltip"|`tree->"tree"|`treegrid->"treegrid"|`treeitem->"treeitem")letrows=int_attr"rows"letrowspan=int_attr"rowspan"letsandboxfmt=string_attr"sandbox"fmtletscopefmt=string_attr"scope"fmtletselected=attr"selected"letshadowrootclonable=attr"shadowrootclonable"letshadowrootdelegatesfocus=attr"shadowrootdelegatesfocus"letshadowrootmodevalue=("shadowrootmode",matchvaluewith|`open_->"open"|`closed->"closed")letshapefmt=string_attr"shape"fmtletsizefmt=string_attr"size"fmtletsizesfmt=string_attr"sizes"fmtletslot_fmt=string_attr"slot"fmtletspan_=int_attr"span"letspellcheck=bool_attr"spellcheck"letsrcfmt=uri_attr"src"fmtletsrcdocfmt=string_attr"srcdoc"fmtletsrclangfmt=string_attr"srclang"fmtletsrcsetfmt=string_attr"srcset"fmtletstart=int_attr"start"letstepfmt=string_attr"step"fmtletstyle_fmt=string_attr~raw:true"style"fmtlettabindex=int_attr"tabindex"lettargetfmt=string_attr"target"fmtlettitle_fmt=string_attr"title"fmtlettranslatevalue=("translate",matchvaluewith|`yes->"yes"|`no->"no")lettype_fmt=string_attr"type"fmtletusemapfmt=string_attr"usemap"fmtletvaluefmt=string_attr"value"fmtletwidthfmt=string_attr"width"fmtletwrapvalue=("wrap",matchvaluewith|`hard->"hard"|`soft->"soft")(* Tags *)letnull=std_tag""[]leta=std_tag"a"letaddress=std_tag"address"letabbr=std_tag"abbr"letarea=void_tag"area"letarticle=std_tag"article"letaside=std_tag"aside"letaudio=std_tag"audio"letb=std_tag"b"letbase=void_tag"base"letbdi=std_tag"bdi"letbdo=std_tag"bdo"letblockquote=std_tag"blockquote"letbr=void_tag"br"letbody=std_tag"body"letbutton=std_tag"button"letcanvas=std_tag"canvas"letcaption=std_tag"caption"letcite=std_tag"cite"letcode=std_tag"code"letcol=void_tag"col"letcolgroup=std_tag"colgroup"letdata=std_tag"data"letdatalist=std_tag"datalist"letdd=std_tag"dd"letdel=std_tag"del"letdetails=std_tag"details"letdfn=std_tag"dfn"letdialog=std_tag"dialog"letdiv=std_tag"div"letdl=std_tag"dl"letdt=std_tag"dt"letem=std_tag"em"letembed=void_tag"embed"letfieldset=std_tag"fieldset"letfigcaption=std_tag"figcaption"letfigure=std_tag"figure"letfooter=std_tag"footer"letform=std_tag"form"leth1=std_tag"h1"leth2=std_tag"h2"leth3=std_tag"h3"leth4=std_tag"h4"leth5=std_tag"h5"leth6=std_tag"h6"lethead=std_tag"head"letheader=std_tag"header"lethgroup=std_tag"hgroup"lethr=void_tag"hr"lethtml=std_tag"html"leti=std_tag"i"letiframe=std_tag"iframe"letimg=void_tag"img"letinput=void_tag"input"letins=std_tag"ins"letkbd=std_tag"kbd"letlabel=std_tag"label"letlegend=std_tag"legend"letli=std_tag"li"letlink=void_tag"link"letmain=std_tag"main"letmap=std_tag"map"letmark=std_tag"mark"letmenu=std_tag"menu"letmeta=void_tag"meta"letmeter=std_tag"meter"letnav=std_tag"nav"letnoscript=std_tag"noscript"letobject_=std_tag"object"letol=std_tag"ol"letoptgroup=std_tag"optgroup"letoptionattrsfmt=text_tag"option"attrsfmtletoutput=std_tag"output"letp=std_tag"p"letpicture=std_tag"picture"letpre=std_tag"pre"letprogress=std_tag"progress"letq=std_tag"q"letrp=std_tag"rp"letrt=std_tag"rt"letruby=std_tag"ruby"lets=std_tag"s"letsamp=std_tag"samp"letscriptattrsfmt=text_tag"script"~raw:trueattrsfmtletsearch=std_tag"search"letsection=std_tag"section"letselect=std_tag"select"letselectedoption=std_tag"selectedoption"letslot=std_tag"slot"letsmall=std_tag"small"letsource=void_tag"source"letspan=std_tag"span"letstrong=std_tag"strong"letstyleattrsfmt=text_tag"style"~raw:trueattrsfmtletsub=std_tag"sub"letsup=std_tag"sup"letsummary=std_tag"summary"lettable=std_tag"table"lettbody=std_tag"tbody"lettd=std_tag"td"lettemplate=std_tag"template"lettextareaattrsfmt=text_tag"textarea"attrsfmtlettfoot=std_tag"tfoot"letth=std_tag"th"letthead=std_tag"thead"lettime=std_tag"time"lettitleattrsfmt=text_tag"title"attrsfmtlettr=std_tag"tr"lettrack=void_tag"track"letu=std_tag"u"letul=std_tag"ul"letvar=std_tag"var"letvideo=std_tag"video"letwbr=void_tag"wbr"endletrecconcatsep=function|[]->HTML.null[]|[node]->node|node::nodes->HTML.null[node;sep;concatsepnodes]moduleSVG=struct(* Attributes *)letdfmt=string_attr"d"fmtletfillfmt=string_attr"fill"fmtletstrokefmt=string_attr"stroke"fmtletstroke_linecapvalue=("stroke-linecap",matchvaluewith|`butt->"butt"|`round->"round"|`square->"square")letstroke_linejoinvalue=("stroke-linejoin",matchvaluewith|`arcs->"arcs"|`bevel->"bevel"|`miter->"miter"|`miter_clip->"miter-clip"|`round->"round")letstroke_widthfmt=string_attr"stroke-width"fmtletviewbox~min_x~min_y~width~height="viewbox",Printf.sprintf"%d %d %d %d"min_xmin_ywidthheightletxmlns="xmlns","http://www.w3.org/2000/svg"(* Tags *)letpath=std_tag"path"letsvg=std_tag"svg"endmoduleAria=structletactivedescendantfmt=string_attr"aria-activedescendant"fmtletatomic=attr"aria-atomic"letautocompletevalue=("aria-autocomplete",matchvaluewith|`inline->"inline"|`list->"list"|`both->"both")letbraillelabelfmt=string_attr"aria-braillelabel"fmtletbrailleroledescriptionfmt=string_attr"aria-brailleroledescription"fmtletbusy=attr"aria-busy"letcheckedvalue=("aria-checked",matchvaluewith|`false_->"false"|`true_->"true"|`mixed->"mixed")letcolcount=int_attr"aria-colcount"letcolindextextfmt=string_attr"aria-colindextext"fmtletcolspan=int_attr"aria-colspan"letcontrolsfmt=string_attr"aria-controls"fmtletcurrentvalue=("aria-current",matchvaluewith|`page->"page"|`step->"step"|`location->"location"|`date->"date"|`time->"time"|`true_->"true")letdescribedbyfmt=string_attr"aria-describedby"fmtletdescriptionfmt=string_attr"aria-description"fmtletdetailsfmt=string_attr"aria-details"fmtletdisabled=attr"aria-disabled"leterrormessagefmt=string_attr"aria-errormessage"fmtletexpanded=bool_attr"aria-expanded"letflowtofmt=string_attr"aria-flowto"fmtlethaspopupvalue=("aria-haspopup",matchvaluewith|`true_->"true"|`menu->"menu"|`listbox->"listbox"|`tree->"tree"|`grid->"grid"|`dialog->"dialog")lethidden=bool_attr"aria-hidden"letinvalidvalue=("aria-invalid",matchvaluewith|`grammar->"grammar"|`spelling->"spelling"|`true_->"true")letkeyshortcutsfmt=string_attr"aria-keyshortcuts"fmtletlabelfmt=string_attr"aria-label"fmtletlabelledbyfmt=string_attr"aria-labelledby"fmtletlevel=int_attr"aria-level"letlivevalue=("aria-live",matchvaluewith|`assertive->"assertive"|`off->"off"|`polite->"polite")letmodal=attr"aria-modal"letmultiline=attr"aria-multiline"letmultiselectable=attr"aria-multiselectable"letorientationvalue=("aria-orientation",matchvaluewith|`horizontal->"horizontal"|`vertical->"vertical")letownsfmt=string_attr"aria-owns"fmtletplaceholderfmt=string_attr"aria-placeholder"fmtletposinset=int_attr"aria-posinset"letpressedvalue=("aria-pressed",matchvaluewith|`false_->"false"|`mixed->"mixed"|`true_->"true")letreadonly=attr"aria-readonly"letrelevantvalue=("aria-relevant",matchvaluewith|`additions->"additions"|`all->"all"|`removals->"removals"|`text->"text")letrequired=attr"aria-required"letroledescriptionfmt=string_attr"aria-roledescription"fmtletrowcount=int_attr"aria-rowcount"letrowindex=int_attr"aria-rowindex"letrowindextextfmt=string_attr"aria-rowindextext"fmtletrowspan=int_attr"aria-rowspan"letselected=bool_attr"aria-selected"letsetsize=int_attr"aria-setsize"letsortvalue=("aria-sort",matchvaluewith|`ascending->"ascending"|`descending->"descending"|`other->"other")letvaluemax=float_attr"aria-valuemax"letvaluemin=float_attr"aria-valuemin"letvaluenow=float_attr"aria-valuenow"letvaluetextfmt=string_attr"aria-valuetext"fmtendmoduleAtom=structletlink=std_tag"atom:link"letxmlns="xmlns:atom","http://www.w3.org/2005/Atom"endmoduleRSS=struct(* Attributes *)letdomainfmt=uri_attr"domain"fmtletlengthv=int_attr"length"vletpathfmt=uri_attr"path"fmtletportv=int_attr"name"vletprotocolfmt=string_attr"protocol"fmtletregister_procedurefmt=string_attr"registerProcedure"fmtlettype_=HTML.type_leturl_fmt=uri_attr"url"fmtletversion_2="version","2.0"(* Tags *)letauthorattrsfmt=text_tag"author"attrsfmtletchannel=std_tag"channel"letcategoryattrsfmt=text_tag"category"attrsfmtletcloudattrs=void_tag"cloud"attrsletcommentsattrsfmt=uri_tag"comments"attrsfmtletcopyrightattrsfmt=text_tag"copyright"attrsfmtletdescriptionattrsfmt=text_tag"description"attrsfmtletdocsattrsfmt=text_tag"docs"attrsfmtletenclosure=std_tag"enclosure"letgeneratorattrsfmt=text_tag"generator"attrsfmtletguidattrsfmt=text_tag"guid"attrsfmtletheightattrsfmt=text_tag"height"attrsfmtletimage=std_tag"image"letitem=std_tag"item"letlanguageattrsfmt=text_tag"language"attrsfmtletlast_build_dateattrsfmt=text_tag"lastBuildDate"attrsfmtletlinkattrsfmt=uri_tag"link"attrsfmtletmanaging_editorattrsfmt=text_tag"managingEditor"attrsfmtletpub_dateattrsfmt=text_tag"pubDate"attrsfmtletrss=std_tag"rss"lettitleattrsfmt=text_tag"title"attrsfmtletttlattrsfmt=text_tag"ttl"attrsfmtleturlattrsfmt=uri_tag"url"attrsfmtletweb_masterattrsfmt=text_tag"webMaster"attrsfmtletwidthattrsfmt=text_tag"width"attrsfmtendmoduleHx=structlet__fmt=string_attr~raw:true"_"fmt(* This is a boolean because it can be selectively switched off in some parts
of the page. *)letboost=bool_attr"data-hx-boost"letconfirmfmt=string_attr"data-hx-confirm"fmtletdeletefmt=uri_attr"data-hx-delete"fmtletdisable=attr"data-hx-disable"letdisabled_eltfmt=string_attr"data-hx-disabled-elt"fmtletdisinheritfmt=string_attr"data-hx-disinherit"fmtletencoding_formdata="data-hx-encoding","multipart/form-data"letextfmt=string_attr"data-hx-ext"fmtletgetfmt=uri_attr"data-hx-get"fmtletheadersfmt=sqattr"data-hx-headers"fmtlethistory_false="data-hx-history","false"lethistory_elt=attr"data-hx-history-elt"letinclude_fmt=string_attr"data-hx-include"fmtletindicatorfmt=string_attr~raw:true"data-hx-indicator"fmtletinherit_fmt=string_attr"data-hx-inherit"fmtletonfmt=string_attr"data-hx-on"~raw:truefmtleton_~eventfmt=string_attr("data-hx-on:"^event)~raw:truefmtletparamsfmt=string_attr"data-hx-params"fmtletpatchfmt=uri_attr"data-hx-patch"fmtletpostfmt=uri_attr"data-hx-post"fmtletpreload=attr"preload"letpreserve=attr"data-hx-preserve"letpromptfmt=string_attr"data-hx-prompt"fmtletpush_urlfmt=uri_attr"data-hx-push-url"fmtletputfmt=uri_attr"data-hx-put"fmtletreplace_urlfmt=string_attr"data-hx-replace-url"fmtletrequestfmt=sqattr"data-hx-request"fmtletselectfmt=string_attr~raw:true"data-hx-select"fmtletselect_oobfmt=string_attr~raw:true"data-hx-select-oob"fmtletsse_closefmt=string_attr"data-sse-close"fmtletsse_connectfmt=string_attr"data-sse-connect"fmtletsse_swapfmt=string_attr"data-sse-swap"fmtletswapfmt=string_attr~raw:true"data-hx-swap"fmtletswap_oobfmt=string_attr~raw:true"data-hx-swap-oob"fmtletsyncfmt=string_attr"data-hx-sync"fmtlettargetfmt=string_attr~raw:true"data-hx-target"fmtlettriggerfmt=string_attr"data-hx-trigger"~raw:truefmtletvalidate=attr"data-hx-validate"letvalsfmt=sqattr"data-hx-vals"fmtletws_connectfmt=string_attr"data-ws-connect"fmtletws_send=attr"data-ws-send"endmoduleMathML=struct(* Attributes *)letaccent=bool_attr"accent"letaccentunder=bool_attr"accentunder"letcolumnspan=int_attr"columnspan"letdepthfmt=string_attr"depth"fmtletdirvalue=("dir",matchvaluewith|`rtl->"rtl"|`ltr->"ltr")letdisplay_block=string_attr"display""block"letdisplaystyle=bool_attr"displaystyle"letfence=bool_attr"fence"letheightfmt=string_attr"height"fmtletlargeop=bool_attr"largeop"letlinethicknessfmt=string_attr"linethickness"fmtletlspacefmt=string_attr"lspace"fmtletmathvariantfmt=string_attr"mathvariant"fmtletmaxsizefmt=string_attr"maxsize"fmtletminsizefmt=string_attr"minsize"fmtletmovablelimits=bool_attr"movablelimits"letrowspan=int_attr"rowspan"letrspacefmt=string_attr"rspace"fmtletscriptlevelfmt=string_attr"scriptlevel"fmtletseparator=bool_attr"separator"letstretchy=bool_attr"stretchy"letsymmetric=bool_attr"symmetric"letvoffsetfmt=string_attr"voffset"fmtletxmlns=string_attr"xmlns""http://www.w3.1998/Math/MathML"(* Tags *)letannotation=std_tag"annotation"letannotation_xml=std_tag"annotation-xml"letmath=std_tag"math"letmerror=std_tag"merror"letmfrac=std_tag"mfrac"letmi=std_tag"mi"letmmultiscripts=std_tag"mmultiscripts"letmn=std_tag"mn"letmo=std_tag"mo"letmover=std_tag"mover"letmpadded=std_tag"mpadded"letmphantom=std_tag"mphantom"letmroot=std_tag"mroot"letmrow=std_tag"mrow"letms=std_tag"ms"letmspace=std_tag"mspace"letmsqrt=std_tag"msqrt"letmstyle=std_tag"mstyle"letmsub=std_tag"msub"letmsubsup=std_tag"msubsup"letmsup=std_tag"msup"letmtable=std_tag"mtable"letmtd=std_tag"mtd"letmtext=std_tag"mtext"letmtr=std_tag"mtr"letmunder=std_tag"munder"letmunderover=std_tag"munderover"letsemantics=std_tag"semantics"end