123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498(* Js_of_ocaml library
* http://www.ocsigen.org/js_of_ocaml/
* Copyright (C) 2014 Hugo Heuzard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, with linking exception;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)openJs_of_ocamlopen!Importletjs_string_of_floatf=(Js.number_of_floatf)##toStringletjs_string_of_inti=(Js.number_of_float(float_of_inti))##toStringmoduletypeXML=Xml_sigs.Twithtypeuri=stringandtypeevent_handler=Dom_html.eventJs.t->boolandtypemouse_event_handler=Dom_html.mouseEventJs.t->boolandtypekeyboard_event_handler=Dom_html.keyboardEventJs.t->boolandtypeelt=Dom.nodeJs.tclasstype['a,'b]weakMap=objectmethodset:'a->'b->unitJs.methmethodget:'a->'bJs.Optdef.tJs.methendletretain=letmap:(Dom.nodeJs.t,Obj.tJs.js_arrayJs.t)weakMapJs.t=letweakMap=Js.Unsafe.global##._WeakMapinnew%jsweakMapinfun(typea)node~(keepme:a)->letprev=Js.Optdef.case(map##getnode)(fun()->new%jsJs.array_empty)(funx->x)inlet(_:int)=prev##push(Obj.reprkeepme)inmap##setnodeprevmoduleXml=structmoduleW=Xml_wrap.NoWraptype'awrap='atype'alist_wrap='alisttypeuri=stringleturi_of_strings=sletstring_of_uris=stypeaname=stringtypeevent_handler=Dom_html.eventJs.t->booltypemouse_event_handler=Dom_html.mouseEventJs.t->booltypekeyboard_event_handler=Dom_html.keyboardEventJs.t->booltypetouch_event_handler=Dom_html.touchEventJs.t->booltypeattrib_k=|Eventofevent_handler|MouseEventofmouse_event_handler|KeyboardEventofkeyboard_event_handler|TouchEventoftouch_event_handler|AttrofJs.js_stringJs.toptionReact.S.ttypeattrib=aname*attrib_kletattrnamev=name,Attr(React.S.const(Somev))letfloat_attribnamevalue:attrib=attrname(js_string_of_floatvalue)letint_attribnamevalue=attrname(js_string_of_intvalue)letstring_attribnamevalue=attrname(Js.stringvalue)letspace_sep_attribnamevalues=attrname(Js.string(String.concat" "values))letcomma_sep_attribnamevalues=attrname(Js.string(String.concat","values))letevent_handler_attribname(value:event_handler)=name,Eventvalueletmouse_event_handler_attribname(value:mouse_event_handler)=name,MouseEventvalueletkeyboard_event_handler_attribname(value:keyboard_event_handler)=name,KeyboardEventvaluelettouch_event_handler_attribname(value:touch_event_handler)=name,TouchEventvalueleturi_attribnamevalue=attrname(Js.stringvalue)leturis_attribnamevalues=attrname(Js.string(String.concat" "values))(** Element *)typeelt=Dom.nodeJs.ttypeename=stringletempty()=(Dom_html.document##createDocumentFragment:>Dom.nodeJs.t)letcommentc=(Dom_html.document##createComment(Js.stringc):>Dom.nodeJs.t)letpcdatas=(Dom_html.document##createTextNode(Js.strings):>Dom.nodeJs.t)letencodedpcdatas=(Dom_html.document##createTextNode(Js.strings):>Dom.nodeJs.t)letentity=letstring_folds~pos~init~f=letr=refinitinfori=postoString.lengths-1doletc=s.[i]inr:=f!rcdone;!rinletinvalid_entitye=failwith(Printf.sprintf"Invalid entity %S"e)inletint_of_char=function|'0'..'9'asx->Some(Char.codex-Char.code'0')|'a'..'f'asx->Some(Char.codex-Char.code'a'+10)|'A'..'F'asx->Some(Char.codex-Char.code'A'+10)|_->Noneinletparse_int~pos~basee=string_folde~pos~init:0~f:(funaccx->matchint_of_charxwith|Somedwhend<base->(acc*base)+d|Some_|None->invalid_entitye)inletis_alpha_num=function|'0'..'9'|'a'..'z'|'A'..'Z'->true|_->falseinfune->letlen=String.lengtheinletstr=iflen>=1&&Char.equale.[0]'#'thenleti=iflen>=2&&(Char.equale.[1]'x'||Char.equale.[1]'X')thenparse_int~pos:2~base:16eelseparse_int~pos:1~base:10einJs.string_constr##fromCharCodeielseifstring_folde~pos:0~init:true~f:(funaccx->(* This is not quite right according to
https://www.xml.com/axml/target.html#NT-Name.
but it seems to cover all html5 entities
https://dev.w3.org/html5/html-author/charref *)acc&&is_alpha_numx)thenmatchewith|"quot"->Js.string"\""|"amp"->Js.string"&"|"apos"->Js.string"'"|"lt"->Js.string"<"|"gt"->Js.string">"|""->invalid_entitye|_->Dom_html.decode_html_entities(Js.string("&"^e^";"))elseinvalid_entityein(Dom_html.document##createTextNodestr:>Dom.nodeJs.t)(* TODO: fix get_prop
it only work when html attribute and dom property names correspond.
find a way to get dom property name corresponding to html attribute
*)letget_propnodename=ifJs.Optdef.test(Js.Unsafe.getnodename)thenSomenameelseNoneletiter_prop_protectednodenamef=matchget_propnodenamewith|Somen->(tryfnwith_->())|None->()letattach_attribsnodel=List.iter(fun(n',att)->letn=Js.stringn'inmatchattwith|Attra->let(keepme:unitReact.S.t)=React.S.map(function|Somev->(ignore(node##setAttributenv);matchn'with|"style"->node##.style##.cssText:=v|_->iter_prop_protectednoden(funname->Js.Unsafe.setnodenamev))|None->(ignore(node##removeAttributen);matchn'with|"style"->node##.style##.cssText:=Js.string""|_->iter_prop_protectednoden(funname->Js.Unsafe.setnodenameJs.null)))ainretain(node:>Dom.nodeJs.t)~keepme|Eventh->Js.Unsafe.setnoden(Js.wrap_callback(funev->Js.bool(hev)))|MouseEventh->Js.Unsafe.setnoden(Js.wrap_callback(funev->Js.bool(hev)))|KeyboardEventh->Js.Unsafe.setnoden(Js.wrap_callback(funev->Js.bool(hev)))|TouchEventh->Js.Unsafe.setnoden(Js.wrap_callback(funev->Js.bool(hev))))lletleaf?(a=[])name=lete=Dom_html.document##createElement(Js.stringname)inattach_attribsea;(e:>Dom.nodeJs.t)letnode?(a=[])namechildren=lete=Dom_html.document##createElement(Js.stringname)inattach_attribsea;List.iter(func->ignore(e##appendChildc))children;(e:>Dom.nodeJs.t)letcdatas=pcdatasletcdata_scripts=cdatasletcdata_styles=cdatasendmoduleXml_Svg=structincludeXmlletleaf?(a=[])name=lete=Dom_html.document##createElementNSDom_svg.xmlns(Js.stringname)inattach_attribsea;(e:>Dom.nodeJs.t)letnode?(a=[])namechildren=lete=Dom_html.document##createElementNSDom_svg.xmlns(Js.stringname)inattach_attribsea;List.iter(func->ignore(e##appendChildc))children;(e:>Dom.nodeJs.t)endmoduleSvg=Svg_f.Make(Xml_Svg)moduleHtml=Html_f.Make(Xml)(Svg)moduleHtml5=HtmlmoduleTo_dom=Tyxml_cast.MakeTo(structtype'aelt='aHtml.eltletelt=Html.toeltend)moduleOf_dom=Tyxml_cast.MakeOf(structtype'aelt='aHtml.eltletelt=Html.totend)moduleRegister=structletremoveChildren(node:#Dom.elementJs.t)=letl=node##.childNodesinfori=0tol##.length-1doJs.Opt.iter(l##itemi)(funx->ignore(node##removeChildx))doneletadd_to?(keep=true)nodecontent=ifnotkeepthenremoveChildrennode;List.iter(funx->Dom.appendChildnode(To_dom.of_elementx))contentletid?keepidcontent=letnode=Dom_html.getElementByIdidinadd_to?keepnodecontentletbody?keepcontent=add_to?keepDom_html.document##.bodycontentlethead?keepcontent=add_to?keepDom_html.document##.headcontentlethtml?headbody=(matchheadwith|Someh->Dom_html.document##.head:=To_dom.of_headh|None->());Dom_html.document##.body:=To_dom.of_bodybody;()endmoduleWrap=structtype'at='aReact.signaltype'atlist='aReactiveData.RList.ttype('a,'b)ft='a->'bletreturn=React.S.constletfmapf=React.S.mapfletnil()=ReactiveData.RList.emptyletsingleton=ReactiveData.RList.singleton_sletconsxxs=ReactiveData.RList.concat(singletonx)xsletmapf=ReactiveData.RList.mapfletappendxy=ReactiveData.RList.concatxyendmoduleUtil=structopenReactiveDataopenRListletinsertAtdomix=letnodes=dom##.childNodesinassert(i<=nodes##.length);ifi=nodes##.lengththenignore(dom##appendChild(x:>Dom.nodeJs.t))elseignore(dom##insertBeforex(nodes##itemi))letmerge_one_patch(dom:Dom.nodeJs.t)(p:Dom.nodeJs.tp)=matchpwith|I(i,x)->leti=ifi<0thendom##.childNodes##.length+1+ielseiininsertAtdomix|Ri->leti=ifi<0thendom##.childNodes##.length+ielseiinletnodes=dom##.childNodesinassert(i>=0&&i<nodes##.length);Js.Opt.iter(nodes##itemi)(funn->Dom.removeChilddomn)|U(i,x)->(leti=ifi<0thendom##.childNodes##.length+ielseiinmatchJs.Opt.to_option(dom##.childNodes##itemi)with|Someold->ignore(dom##replaceChildxold)|_->assertfalse)|X(i,move)->(leti=ifi<0thendom##.childNodes##.length+ielseiinifmove=0then()elsematchJs.Opt.to_option(dom##.childNodes##itemi)with|Somei'->insertAtdom(i+ifmove>0thenmove+1elsemove)i'|_->assertfalse)letrecremoveChildrendom=matchJs.Opt.to_optiondom##.lastChildwith|None->()|Somec->ignore(dom##removeChildc);removeChildrendomletmerge_msg(dom:Dom.nodeJs.t)(msg:Dom.nodeJs.tmsg)=matchmsgwith|Setl->(* Format.eprintf "replace all@."; *)removeChildrendom;List.iter(funl->ignore(dom##appendChildl))l|Patchp->(* Format.eprintf "patch@."; *)List.iter(merge_one_patchdom)pletupdate_children(dom:Dom.nodeJs.t)(nodes:Dom.nodeJs.tt)=removeChildrendom;letkeepme:unitReact.S.t=fold(fun()msg->merge_msgdommsg)nodes()inretain(dom:Dom.nodeJs.t)~keepme;()endmoduleR=structletfilter_attrib(name,a)on=matchawith|Xml.Event_|Xml.MouseEvent_|Xml.KeyboardEvent_|Xml.TouchEvent_->raise(Invalid_argument"filter_attrib not implemented for event handler")|Xml.Attra->name,Xml.Attr(React.S.l2(funona->ifonthenaelseNone)ona)letattach_attribs=Xml.attach_attribsmoduleXml=structmoduleW=Wraptype'awrap='aW.ttype'alist_wrap='aW.tlisttypeuri=Xml.uriletstring_of_uri=Xml.string_of_urileturi_of_string=Xml.uri_of_stringtypeaname=Xml.anametypeevent_handler=Xml.event_handlertypemouse_event_handler=Xml.mouse_event_handlertypekeyboard_event_handler=Xml.keyboard_event_handlertypetouch_event_handler=Xml.touch_event_handlertypeattrib=Xml.attribletattrnamefs=leta=W.fmapfsinname,Xml.Attraletfloat_attribnames=attrname(funf->Some(js_string_of_floatf))sletint_attribnames=attrname(funf->Some(js_string_of_intf))sletstring_attribnames=attrname(funf->Some(Js.stringf))sletspace_sep_attribnames=attrname(funf->Some(Js.string(String.concat" "f)))sletcomma_sep_attribnames=attrname(funf->Some(Js.string(String.concat","f)))sletevent_handler_attribnames=Xml.event_handler_attribnamesletmouse_event_handler_attribnames=Xml.mouse_event_handler_attribnamesletkeyboard_event_handler_attribnames=Xml.keyboard_event_handler_attribnameslettouch_event_handler_attribnames=Xml.touch_event_handler_attribnamesleturi_attribnames=attrname(funf->Some(Js.stringf))sleturis_attribnames=attrname(funf->Some(Js.string(String.concat" "f)))stypeelt=Xml.elttypeename=Xml.enameletempty=Xml.emptyletcomment=Xml.commentletpcdatas=lete=Dom_html.document##createTextNode(Js.string"")inletkeepme=React.S.map(funs->e##.data:=Js.strings)sinretain(e:>Dom.nodeJs.t)~keepme;(e:>Dom.nodeJs.t)letencodedpcdatas=pcdatasletentitys=Xml.entitysletleaf=Xml.leafletnode?(a=[])namel=lete=Dom_html.document##createElement(Js.stringname)inattach_attribsea;Util.update_children(e:>Dom.nodeJs.t)l;(e:>Dom.nodeJs.t)letcdata=Xml.cdataletcdata_script=Xml.cdata_scriptletcdata_style=Xml.cdata_styleendmoduleXml_Svg=structincludeXmlletleaf=Xml_Svg.leafletnode?(a=[])namel=lete=Dom_html.document##createElementNSDom_svg.xmlns(Js.stringname)inattach_attribsea;Util.update_children(e:>Dom.nodeJs.t)l;(e:>Dom.nodeJs.t)endmoduleSvg=Svg_f.Make(Xml_Svg)moduleHtml=Html_f.Make(Xml)(Svg)moduleHtml5=Htmlend