12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007(* Copyright 2023 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|Commentofstringtype'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_attrp=function|"",_->()|name,""->p"\n";pname|name,value->p"\n";pname;p{|="|};pvalue;p{|"|}(* Loosely based on https://www.w3.org/TR/DOM-Parsing/ *)letrecwrite_tag~xmlp=function|Tag{name="";children=Somechildren;_}->List.iter(write_tag~xmlp)children|Tag{name;attrs;children=Some[]}whenxml->p"<";pname;List.iter(write_attrp)attrs;p" />"|Tag{name;attrs;children=None}->p"<";pname;List.iter(write_attrp)attrs;p(ifxmlthen" />"else">")|Tag{name;attrs;children=Somechildren}->ifname="html"thenp"<!DOCTYPE html>\n";p"<";pname;List.iter(write_attrp)attrs;p">";List.iter(write_tag~xmlp)children;p"</";pname;p">"|Txtstr->pstr|Commentstr->p"<!-- ";pstr;p" -->"letto_string~xmlnode=letbuf=Buffer.create256inwrite_tag~xml(Buffer.add_stringbuf)node;Buffer.contentsbufletppppfnode=node|>to_string~xml:false|>Format.pp_print_stringppfletto_xml=to_string~xml:trueletto_string=to_string~xml:falseletpp_xmlppfnode=node|>to_xml|>Format.pp_print_stringppfletrespond?status?code?headersnode=Dream.html?status?code?headers(to_stringnode)letsend?text_or_binary?end_of_messagewebsocketnode=Dream.send?text_or_binary?end_of_messagewebsocket(to_stringnode)letset_bodyrespnode=Dream.set_bodyresp(to_stringnode);Dream.set_headerresp"Content-Type""text/html"letwritestreamnode=Dream.writestream(to_stringnode)lettxt_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"""|c->Buffer.add_charbuffercletattr_escaperaws=ifrawthenselseletbuffer=Buffer.create(String.lengths*2)inString.iter(attr_escapebuffer)s;Buffer.contentsbufferletattrname=name,""letstring_attrname?(raw=false)fmt=Printf.ksprintf(funs->name,attr_escaperaws)fmtleturi_attrnamefmt=Printf.ksprintf(funs->name,s|>Uri.of_string|>Uri.to_string|>attr_escapefalse)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)]})fmtlettxt?(raw=false)fmt=Printf.ksprintf(funs->Txt(txt_escaperaws))fmtletcsrf_tagreq=req|>Dream.csrf_tag|>txt~raw:true"%s"letcommentstr=Comment(txt_escapefalsestr)let(+@)nodeattr=matchnodewith|Tagt->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=function|Tag{name="";_}->true|_->falseletis_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"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"fmtletcontenteditable=bool_attr"contenteditable"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"fmtletintegrityfmt=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"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"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:trueattrsfmtletsection=std_tag"section"letselect=std_tag"select"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"endmoduleSVG=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=string_attr"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"|`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"fmtendmoduleHx=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"letdisinheritfmt=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=string_attr"data-hx-headers"fmtlethistory_false=bool_attr"data-hx-history"falselethistory_elt=attr"data-hx-history-elt"letinclude_fmt=string_attr"data-hx-include"fmtletindicatorfmt=string_attr~raw:true"data-hx-indicator"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=string_attr"data-hx-request"fmtletselectfmt=string_attr~raw:true"data-hx-select"fmtletselect_oobfmt=string_attr~raw:true"data-hx-select-oob"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=string_attr"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"endmoduleLivereload=structletenabled=matchSys.getenv"LIVERELOAD"with|"1"->true|_|(exception_)->falseletendpoint="/_livereload"letscript=ifenabledthenHTML.script[]{|
(() => {
const retryIntervalMs = 500;
const socketUrl = `ws://${location.host}%s`;
const s = new WebSocket(socketUrl);
s.onopen = _evt => {
console.debug("Live reload: WebSocket connection open");
};
s.onclose = _evt => {
console.debug("Live reload: WebSocket connection closed");
function reload() {
const s2 = new WebSocket(socketUrl);
s2.onerror = _evt => {
setTimeout(reload, retryIntervalMs);
};
s2.onopen = _evt => {
location.reload();
};
};
reload();
};
s.onerror = evt => {
console.debug("Live reload: WebSocket error:", evt);
};
})()
|}endpointelseHTML.null[]letroute=Dream.getendpoint(funreq->ifenabledthenDream.websocket(funsock->Lwt.bind(Dream.receivesock)(fun_->Dream.close_websocketsock))elseDream.not_foundreq)end