123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769# 1 "src/lib/eliom_client_core.client.ml"(* Ocsigen
* http://www.ocsigen.org
* Copyright (C) 2010 Vincent Balat
* Copyright (C) 2011 Jérôme Vouillon, Grégoire Henry, Pierre Chambart
* Copyright (C) 2012 Benedikt Becker
*
* 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_ocamlopenEliom_libmoduleXml=Eliom_content_core.Xml(* Logs *)letsection=Lwt_log.Section.make"eliom:client"(* == Auxiliaries *)letcreate_buffer()=letstack=ref[]inletelts=ref[]inletaddx=elts:=x::!eltsandget()=List.rev!eltsinletpush()=stack:=!elts::!stack;elts:=[]inletflush()=letres=get()in(match!stackwith|l::r->elts:=l;stack:=r|[]->elts:=[]);resinadd,get,flush,push(* == Closure *)moduleClient_closure:sigvalregister:closure_id:string->closure:(_->_)->unitvalfind:closure_id:string->poly->polyend=structletclient_closures=Jstable.create()letregister~closure_id~closure=Jstable.addclient_closures(Js.stringclosure_id)(from_poly(to_polyclosure))letfind~closure_id=Js.Optdef.get(Jstable.findclient_closures(Js.stringclosure_id))(fun()->raiseNot_found)endmoduleClient_value:sigvalfind:instance_id:int->polyoptionvalinitialize:Eliom_runtime.client_value_datum->unitend=structlettable=new%jsJs.array_emptyletfind~instance_id=ifinstance_id=0then(* local client value *)NoneelseJs.Optdef.to_option(Js.array_gettableinstance_id)letinitialize{Eliom_runtime.closure_id;args;value=server_value}=letclosure=tryClient_closure.find~closure_idwithNot_found->letpos=matchEliom_runtime.Client_value_server_repr.locserver_valuewith|None->""|Somep->Printf.sprintf"(%s)"(Eliom_lib.pos_to_stringp)inLwt_log.raise_error_f~section"Client closure %s not found %s (is the module linked on the client?)"closure_idposinletvalue=closureargsinEliom_unwrap.late_unwrap_valueserver_valuevalue;(* Only register global client values *)letinstance_id=Eliom_runtime.Client_value_server_repr.instance_idserver_valueinifinstance_id<>0thenJs.array_settableinstance_idvalueendletmiddleClickev=matchDom_html.taggedEventevwith|Dom_html.MouseEventev->Dom_html.buttonPressedev=Dom_html.Middle_button||Js.to_boolev##.ctrlKey||Js.to_boolev##.shiftKey||Js.to_boolev##.altKey||Js.to_boolev##.metaKey|_->falsemoduleInjection:sigvalget:?ident:string->?pos:pos->name:string->_valinitialize:compilation_unit_id:string->Eliom_client_value.injection_datum->unitend=structlettable=Jstable.create()letget?ident?pos~name=Lwt_log.ign_debug_f~section"Get injection %s"name;from_poly(Js.Optdef.get(Jstable.findtable(Js.stringname))(fun()->letname=matchident,poswith|None,None->Printf.sprintf"%s"name|None,Somepos->Printf.sprintf"%s at %s"name(Eliom_lib.pos_to_stringpos)|Somei,None->Printf.sprintf"%s (%s)"namei|Somei,Somepos->Printf.sprintf"%s (%s at %s)"namei(Eliom_lib.pos_to_stringpos)inLwt_log.raise_error_f"Did not find injection %s"name))letinitialize~compilation_unit_id{Eliom_runtime.injection_id;injection_value;_}=Lwt_log.ign_debug_f~section"Initialize injection %d"injection_id;(* BBB One should assert that injection_value doesn't contain any
value marked for late unwrapping. How to do this efficiently? *)Jstable.addtable(Js.string(compilation_unit_id^string_of_intinjection_id))injection_valueend(* == Populating client values and injections by global data *)typecompilation_unit_global_data={mutableserver_section:Eliom_runtime.client_value_datumarraylist;mutableclient_section:Eliom_runtime.injection_datumarraylist}letglobal_data=refString_map.emptyletdo_next_server_section_data~compilation_unit_id=Lwt_log.ign_debug_f~section"Do next client value data section in compilation unit %s"compilation_unit_id;tryletdata=String_map.findcompilation_unit_id!global_datainmatchdata.server_sectionwith|l::r->data.server_section<-r;Array.iterClient_value.initializel|[]->Lwt_log.raise_error_f~section"Queue of client value data for compilation unit %s is empty (is it linked on the server?)"compilation_unit_idwithNot_found->()(* Client-only compilation unit *)letdo_next_client_section_data~compilation_unit_id=Lwt_log.ign_debug_f~section"Do next injection data section in compilation unit %s"compilation_unit_id;tryletdata=String_map.findcompilation_unit_id!global_datainmatchdata.client_sectionwith|l::r->data.client_section<-r;Array.iter(funi->Injection.initialize~compilation_unit_idi)l|[]->Lwt_log.raise_error_f~section"Queue of injection data for compilation unit %s is empty (is it linked on the server?)"compilation_unit_idwithNot_found->()(* Client-only compilation unit *)(*******************************************************************************)letregister_unwrapped_elt,force_unwrapped_elts=letsuspended_nodes=ref[]in((funelt->suspended_nodes:=elt::!suspended_nodes),fun()->Lwt_log.ign_debug~section"Force unwrapped elements";List.iterXml.force_lazy!suspended_nodes;suspended_nodes:=[])(* == Process nodes
(a.k.a. nodes with a unique Dom instance on each client process) *)letregister_process_node,find_process_node=letprocess_nodes:Dom.nodeJs.tJstable.t=Jstable.create()inletfindid=Lwt_log.ign_debug_f~section"Find process node %a"(fun()->Js.to_string)id;Jstable.findprocess_nodesidinletregisteridnode=Lwt_log.ign_debug_f~section"Register process node %a"(fun()->Js.to_string)id;letnode=ifnode##.nodeName##toLowerCase==Js.string"script"then(* We don't want to reexecute global scripts. *)(Dom_html.document##(createTextNode(Js.string"")):>Dom.nodeJs.t)elsenodeinJstable.addprocess_nodesidnodeinregister,findletregistered_process_nodeid=Js.Optdef.test(find_process_nodeid)letgetElementByIdid=Js.Optdef.case(find_process_node(Js.stringid))(fun()->Lwt_log.ign_warning_f~section"getElementById %s: Not_found"id;raiseNot_found)(funpnode->pnode)(* == Request nodes
(a.k.a. nodes with a unique Dom instance in the current request) *)letregister_request_node,find_request_node,reset_request_nodes=letrequest_nodes:Dom.nodeJs.tJstable.tref=ref(Jstable.create())inletfindid=Jstable.find!request_nodesidinletregisteridnode=Lwt_log.ign_debug_f~section"Register request node %a"(fun()->Js.to_string)id;Jstable.add!request_nodesidnodeinletreset()=Lwt_log.ign_debug~section"Reset request nodes";(* Unwrapped elements must be forced
before resetting the request node table. *)force_unwrapped_elts();request_nodes:=Jstable.create()inregister,find,reset(* == Organize the phase of loading or change_page
In the following functions, onload referrers the initial loading phase
*and* to the change_page phase
*and* to the loading phase after caml services (added 2016-03 --V). *)letload_mutex=Lwt_mutex.create()let_=ignore(Lwt_mutex.lockload_mutex)letin_onload,broadcast_load_end,wait_load_end,set_loading_phase=letloading_phase=reftrueinletload_end=Lwt_condition.create()inletset()=loading_phase:=trueinletin_onload()=!loading_phaseinletbroadcast_load_end()=loading_phase:=false;Lwt_condition.broadcastload_end()inletwait_load_end()=if!loading_phasethenLwt_condition.waitload_endelseLwt.return_unitinin_onload,broadcast_load_end,wait_load_end,set(* == Helper's functions for Eliom's event handler.
Allow conversion of Xml.event_handler to javascript closure and
their registration in Dom node.
*)(* forward declaration... *)letchange_page_uri_:(?cookies_info:bool*stringlist->?tmpl:string->string->unit)ref=ref(fun?cookies_info:_?tmpl:__href->assertfalse)letchange_page_get_form_:(?cookies_info:bool*stringlist->?tmpl:string->Dom_html.formElementJs.t->string->unit)ref=ref(fun?cookies_info:_?tmpl:__form_href->assertfalse)letchange_page_post_form_=ref(fun?cookies_info:_?tmpl:__form_href->assertfalse)typeclient_form_handler=Dom_html.eventJs.t->boolLwt.tletraw_a_handlernodecookies_infotmplev=lethref=(Js.Unsafe.coercenode:Dom_html.anchorElementJs.t)##.hrefinlethttps=Url.get_ssl(Js.to_stringhref)in(* Returns true when the default link behaviour is to be kept: *)middleClickev||(not!Eliom_common.is_client_app)&&((https=Sometrue&¬Eliom_request_info.ssl_)||(https=Somefalse&&Eliom_request_info.ssl_))||((* If a link is clicked, we do not want to continue propagation
(for example if the link is in a wider clickable area) *)Dom_html.stopPropagationev;!change_page_uri_?cookies_info?tmpl(Js.to_stringhref);false)letraw_form_handlerformkindcookies_infotmplevclient_form_handler=letaction=Js.to_stringform##.actioninlethttps=Url.get_sslactioninletchange_page_form=matchkindwith|`Form_get->!change_page_get_form_|`Form_post->!change_page_post_form_inletf()=Lwt.async@@fun()->let%lwtb=client_form_handlerevinifnotbthenchange_page_form?cookies_info?tmplformaction;Lwt.return_unitin(not!Eliom_common.is_client_app)&&((https=Sometrue&¬Eliom_request_info.ssl_)||(https=Somefalse&&Eliom_request_info.ssl_))||(f();false)letraw_event_handlervalue=lethandler=(*XXX???*)(Eliom_lib.from_poly(Eliom_lib.to_polyvalue):#Dom_html.eventJs.t->unit)infunev->tryhandlerev;truewithEliom_client_value.False->falseletclosure_name_prefix=Eliom_runtime.RawXML.closure_name_prefixletclosure_name_prefix_len=String.lengthclosure_name_prefixletreify_caml_eventnamenodece=matchcewith|Xml.CE_call_serviceNone->name,`Other(fun_->true)|Xml.CE_call_service(Some(`A,cookies_info,tmpl,_))->(name,`Other(funev->letnode=Js.Opt.get(Dom_html.CoerceTo.anode)(fun()->Lwt_log.raise_error~section"not an anchor element")inraw_a_handlernodecookies_infotmplev))|Xml.CE_call_service(Some(((`Form_get|`Form_post)askind),cookies_info,tmpl,client_hdlr))->(name,`Other(funev->letform=Js.Opt.get(Dom_html.CoerceTo.formnode)(fun()->Lwt_log.raise_error~section"not a form element")inraw_form_handlerformkindcookies_infotmplev(Eliom_lib.from_polyclient_hdlr:client_form_handler)))|Xml.CE_client_closuref->(name,`Other(funev->tryfev;truewithEliom_client_value.False->false))|Xml.CE_client_closure_keyboardf->(name,`Keyboard(funev->tryfev;truewithEliom_client_value.False->false))|Xml.CE_client_closure_touchf->(name,`Touch(funev->tryfev;truewithEliom_client_value.False->false))|Xml.CE_client_closure_mousef->(name,`Mouse(funev->tryfev;truewithEliom_client_value.False->false))|Xml.CE_registered_closure(_,cv)->letname=letlen=String.lengthnameiniflen>closure_name_prefix_len&&String.subname0closure_name_prefix_len=closure_name_prefixthenString.subnameclosure_name_prefix_len(len-closure_name_prefix_len)elsenameinname,`Other(raw_event_handlercv)letregister_event_handler,flush_load_script=letadd,_,flush,_=create_buffer()inletregisternode(name,ev)=matchreify_caml_eventnamenodeevwith|"onload",`Otherf->addf|"onload",`Keyboard_->failwith"keyboard event handler for onload"|"onload",`Touch_->failwith"touch event handler for onload"|"onload",`Mouse_->failwith"mouse event handler for onload"|name,`Otherf->Js.Unsafe.setnode(Js.bytestringname)(Dom_html.handler(funev->Js.bool(fev)))|name,`Keyboardf->Js.Unsafe.setnode(Js.bytestringname)(Dom_html.handler(funev->Js.bool(fev)))|name,`Touchf->Js.Unsafe.setnode(Js.bytestringname)(Dom_html.handler(funev->Js.bool(fev)))|name,`Mousef->Js.Unsafe.setnode(Js.bytestringname)(Dom_html.handler(funev->Js.bool(fev)))inletflush()=letfs=flush()inletev=Eliommod_dom.createEvent(Js.string"load")inignore(List.for_all(funf->fev)fs)inregister,flushletrebuild_attrib_val=function|Xml.AFloatf->(Js.number_of_floatf)##toString|Xml.AInti->(Js.number_of_float(float_of_inti))##toString|Xml.AStrs->Js.strings|Xml.AStrL(Xml.Space,sl)->Js.string(String.concat" "sl)|Xml.AStrL(Xml.Comma,sl)->Js.string(String.concat","sl)letclass_list_of_racontent=function|Xml.AStrs->[s]|Xml.AStrL(_space,l)->l|_->failwith"attribute class is not a string"letclass_list_of_racontent_o=function|Somec->class_list_of_racontentc|None->[]letrebuild_class_listl1l2l3=letfs=(not(List.exists((=)s)l2))&¬(List.exists((=)s)l3)inl3@List.filterfl1letrebuild_class_stringl1l2l3=rebuild_class_listl1l2l3|>String.concat" "|>Js.string(* html attributes and dom properties use different names
**example**: maxlength vs maxLenght (case sensitive).
- Before dom react, it was enough to set html attributes only as
there were no update after creation.
- Dom React may update attributes later.
Html attrib changes are not taken into account if the corresponding
Dom property is defined.
**example**: updating html attribute `value` has no effect
if the dom property `value` has be set by the user.
=WE NEED TO SET DOM PROPERTIES=
-Tyxml only gives us html attribute names and we can set them safely.
-The name for dom properties is maybe different.
We set it only if we find out that the property
match_the_attribute_name / is_already_defined (get_prop).
*)(* 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_propnodenamef=matchget_propnodenamewithSomen->fn|None->()letiter_prop_protectednodenamef=matchget_propnodenamewith|Somen->(tryfnwith_->())|None->()letspace_re=Regexp.regexp" "letcurrent_classesnode=letname=Js.string"class"inJs.Opt.casenode##(getAttributename)(fun()->[])(funs->Js.to_strings|>Regexp.(splitspace_re))letrebuild_reactive_class_rattribnodes=letname=Js.string"class"inlete=React.S.diff(funvv'->v',v)sandf(v,v')=letl1=current_classesnodeandl2=class_list_of_racontent_ovandl3=class_list_of_racontent_ov'inlets=rebuild_class_stringl1l2l3innode##(setAttributenames);iter_propnodename(funname->Js.Unsafe.setnodenames)inf(None,React.S.values);Dom_reference.retainnode~keep:(React.E.mapfe)letrecrebuild_rattribnodera=matchXml.racontentrawith|Xml.RAawhenXml.anamera="class"->letl1=current_classesnodeandl2=class_list_of_racontentainletname=Js.string"class"ands=rebuild_class_stringl1l2l2innode##(setAttributenames)|Xml.RAa->letname=Js.string(Xml.anamera)inletv=rebuild_attrib_valainnode##(setAttributenamev)|Xml.RAReactswhenXml.anamera="class"->rebuild_reactive_class_rattribnodes|Xml.RAReacts->letname=Js.string(Xml.anamera)inDom_reference.retainnode~keep:(React.S.map(function|None->node##(removeAttributename);iter_prop_protectednodename(funname->Js.Unsafe.setnodenameJs.null)|Somev->letv=rebuild_attrib_valvinnode##(setAttributenamev);iter_prop_protectednodename(funname->Js.Unsafe.setnodenamev))s)|Xml.RACamlEventHandlerev->register_event_handlernode(Xml.anamera,ev)|Xml.RALazyStrs->node##(setAttribute(Js.string(Xml.anamera))(Js.strings))|Xml.RALazyStrL(Xml.Space,l)->node##(setAttribute(Js.string(Xml.anamera))(Js.string(String.concat" "l)))|Xml.RALazyStrL(Xml.Comma,l)->node##(setAttribute(Js.string(Xml.anamera))(Js.string(String.concat","l)))|Xml.RAClient(_,_,value)->rebuild_rattribnode(Eliom_lib.from_poly(Eliom_lib.to_polyvalue):Xml.attrib)(* TODO: Registering a global "onunload" event handler breaks the
'bfcache' mechanism of Firefox and Safari. We may try to use
"pagehide" whenever this event exists. See:
https://developer.mozilla.org/En/Using_Firefox_1.5_caching
http://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/
and the function [Eliommod_dom.test_pageshow_pagehide]. *)letdelayf=Lwt.ignore_result(Lwt.pause()>>=fun()->f();Lwt.return_unit)moduleReactState:sigtypetvalstart_signal:(t->unitReact.signal)->Dom.nodeJs.tvalchange_dom:t->Dom.nodeJs.t->unitend=struct(*
ISSUE
=====
There is a conflict when many dom react are inside each other.
let s_lvl1 = S.map (function
| case1 -> ..
| case2 -> let s_lvl2 = ... in R.node s_lvl2) ...
in R.node s_lvl1
both dom react will update the same dom element (call it `dom_elt`) and
we have to prevent an (outdated) s_lvl2 signal
to replace `dom_elt` (updated last by a s_lvl1 signal)
SOLUTION
========
- we associate to the dom element an array of the signals that may update it
- when a dom element is updated, we transfer the signals to the appropriate
element: outer dom react are moved to the new element while inner dom react
are left to the old element.
*)classtype['a,'b]weakMap=objectmethodset:'a->'b->unitJs.methmethodget:'a->'bJs.Optdef.tJs.methendtypet={mutablenode:Dom.nodeJs.toption;mutablesignal:unitReact.S.toption}[@@warning"-69"]letsignals:(Dom.nodeJs.t,tarray)weakMapJs.t=letweakMap=Js.Unsafe.global##._WeakMapinnew%jsweakMapletget_signals(elt:Dom.nodeJs.t):tarray=Js.Optdef.get(signals##getelt)(fun()->[||])letset_signals(elt:Dom.nodeJs.t)(a:tarray)=signals##seteltaletsignal_indexida=letrecfind_recidali=assert(i<l);ifid==a.(i)thenielsefind_recidal(i+1)infind_recida(Array.lengtha)0letstart_signalf=letstate={node=None;signal=None}instate.signal<-Some(fstate);matchstate.nodewithSomedom->dom|None->assertfalseletchange_domstatedom=matchstate.nodewith|None->state.node<-Somedom;set_signalsdom(Array.append[|state|](get_signalsdom))|Somedom'->letsignals'=get_signalsdom'inleti=signal_indexstatesignals'inletsignals=get_signalsdominset_signalsdom'(Array.subsignals'(i+1)(Array.lengthsignals'-i-1));letparent_signals=Array.subsignals'0(i+1)inArray.iter(funstate->state.node<-Somedom)parent_signals;set_signalsdom(Array.appendparent_signalssignals);Js.Opt.casedom'##.parentNode(fun()->(* no parent -> no replace needed *)())(funparent->Js.Opt.iter(Dom.CoerceTo.elementparent)(funparent->(* really update the dom *)ignore(Dom_html.elementparent)##(replaceChilddomdom')))endtypecontent_ns=[`HTML5|`SVG]letrecrebuild_node'nselt=matchXml.get_nodeeltwith|Xml.DomNodenode->(* assert (Xml.get_node_id node <> NoId); *)node|Xml.ReactChildren(node,elts)->letdom=raw_rebuild_nodensnodeinJs_of_ocaml_tyxml.Tyxml_js.Util.update_childrendom(ReactiveData.RList.map(rebuild_node'ns)elts);Xml.set_dom_nodeeltdom;dom|Xml.ReactNodesignal->letdom=ReactState.start_signal(funstate->React.S.map(funelt'->letdom=rebuild_node'nselt'inXml.set_dom_nodeeltdom;ReactState.change_domstatedom)signal)inXml.set_dom_nodeeltdom;dom|Xml.TyXMLNoderaw_elt->(matchXml.get_node_ideltwith|Xml.NoId->raw_rebuild_nodensraw_elt|Xml.RequestId_->(* Do not look in request_nodes hashtbl: such elements have
been bind while unwrapping nodes. *)letnode=raw_rebuild_nodensraw_eltinXml.set_dom_nodeeltnode;node|Xml.ProcessIdid->letid=Js.stringidinJs.Optdef.case(find_process_nodeid)(fun()->letnode=raw_rebuild_nodens(Xml.contentelt)inregister_process_nodeidnode;node)(funn->(n:>Dom.nodeJs.t)))andraw_rebuild_nodens=function|Xml.Empty|Xml.Comment_->(* FIXME *)(Dom_html.document##(createTextNode(Js.string"")):>Dom.nodeJs.t)|Xml.EncodedPCDATAs|Xml.PCDATAs->(Dom_html.document##(createTextNode(Js.strings)):>Dom.nodeJs.t)|Xml.Entitys->letentity=Dom_html.decode_html_entities(Js.string("&"^s^";"))in(Dom_html.document##(createTextNodeentity):>Dom.nodeJs.t)|Xml.Leaf(name,attribs)->letnode=Dom_html.document##(createElement(Js.stringname))inList.iter(rebuild_rattribnode)attribs;(node:>Dom.nodeJs.t)|Xml.Node(name,attribs,childrens)->letns=ifname="svg"then`SVGelsensinletnode=matchnswith|`HTML5->Dom_html.document##(createElement(Js.stringname))|`SVG->letsvg_ns="http://www.w3.org/2000/svg"inDom_html.document##(createElementNS(Js.stringsvg_ns)(Js.stringname))inList.iter(rebuild_rattribnode)attribs;List.iter(func->Dom.appendChildnode(rebuild_node'nsc))childrens;(node:>Dom.nodeJs.t)(* [is_before_initial_load] tests whether it is executed before the
loading of the initial document, e.g. during the initialization of the
(OCaml) module, i.e. before [Eliom_client_main.onload]. *)letis_before_initial_load,set_initial_load=letbefore_load=reftruein(fun()->!before_load),fun()->before_load:=falseletrebuild_node_nsnscontextelt'=Lwt_log.ign_debug_f~section"Rebuild node %a (%s)"(fun()e->Eliom_content_core.Xml.string_of_node_id(Xml.get_node_ide))elt'context;ifis_before_initial_load()thenLwt_log.raise_error_f~section~inspect:(rebuild_node'nselt')"Cannot apply %s%s before the document is initially loaded"contextXml.(matchget_node_idelt'with|NoId->" "|RequestIdid->" on request node "^id|ProcessIdid->" on global node "^id);letnode=Js.Unsafe.coerce(rebuild_node'nselt')inflush_load_script();nodeletrebuild_node_svgcontextelt=letelt'=Eliom_content_core.Svg.F.toelteltinrebuild_node_ns`SVGcontextelt'(** The first argument describes the calling function (if any) in case
of an error. *)letrebuild_nodecontextelt=letelt'=Eliom_content_core.Html.F.toelteltinrebuild_node_ns`HTML5contextelt'(******************************************************************************)moduleSyntax_helpers=structletregister_client_closureclosure_idclosure=Client_closure.register~closure_id~closureletopen_client_sectioncompilation_unit_id=do_next_server_section_data~compilation_unit_id;do_next_client_section_data~compilation_unit_idletclose_server_sectioncompilation_unit_id=do_next_server_section_data~compilation_unit_idletget_escaped_value=from_polyletget_injection?ident?posname=Injection.get?ident?pos~nameend