123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792(* Ocsigen
* http://www.ocsigen.org
* Copyright (C) 2011 Pierre Chambart, Grégoire Henry
*
* 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_libletsection=Lwt_log.Section.make"eliom:dom"letiter_nodeListnodeListf=fori=0tonodeList##.length-1do(* Unsafe.get is ten time faster than nodeList##item *)f(Js.Unsafe.getnodeListi)doneletiter_attrList(attrList:Dom.attrDom.namedNodeMapJs.t)(f:Dom.attrJs.t->unit)=fori=0toattrList##.length-1do(* Unsafe.get is ten time faster than nodeList##item.
Is it the same for attrList ? *)(* let v = attrList##item(i) in *)letv=Js.Unsafe.getattrListiin(* IE8 provides [null] in node##attributes;
so we wrap v to be a Js.opt *)Js.Opt.itervfdone(* Dummy type used in the following "test_*" functions to test the
presence of methods in various browsers. *)classtypedom_tester=objectmethodcompareDocumentPosition:unitJs.optdefJs.propmethodquerySelectorAll:unitJs.optdefJs.propmethodclassList:unitJs.optdefJs.propmethodcreateEvent:unitJs.optdefJs.propmethodonpageshow:unitJs.optdefJs.propmethodonpagehide:unitJs.optdefJs.propmethodonhashchange:unitJs.optdefJs.propendlettest_querySelectorAll()=Js.Optdef.test(Js.Unsafe.coerceDom_html.document:dom_testerJs.t)##.querySelectorAlllettest_compareDocumentPosition()=Js.Optdef.test(Js.Unsafe.coerceDom_html.document:dom_testerJs.t)##.compareDocumentPositionlettest_classList()=Js.Optdef.test(Js.Unsafe.coerceDom_html.document##.documentElement:dom_testerJs.t)##.classListlettest_createEvent()=Js.Optdef.test(Js.Unsafe.coerceDom_html.document:dom_testerJs.t)##.createEventlettest_pageshow_pagehide()=lettester=(Js.Unsafe.coerceDom_html.window:dom_testerJs.t)inJs.Optdef.testtester##.onpageshow&&Js.Optdef.testtester##.onpagehidelettest_onhashchange()=Js.Optdef.test(Js.Unsafe.coerceDom_html.window:dom_testerJs.t)##.onhashchangeletfast_ancessor(elt1:#Dom.nodeJs.t)(elt2:#Dom.nodeJs.t)=letopenDom.DocumentPositioninhaselt1##(compareDocumentPosition(elt2:>Dom.nodeJs.t))contained_byletslow_ancessor(elt1:#Dom.nodeJs.t)(elt2:#Dom.nodeJs.t)=letreccheck_parentn=ifn==(elt1:>Dom.nodeJs.t)thentrueelsematchJs.Opt.to_optionn##.parentNodewith|None->false|Somep->check_parentpincheck_parent(elt2:>Dom.nodeJs.t)letancessor=iftest_compareDocumentPosition()thenfast_ancessorelseslow_ancessorletfast_select_request_nodesroot=root##(querySelectorAll(Js.string("."^Eliom_runtime.RawXML.request_node_class)))letfast_select_nodesroot=if!Eliom_config.debug_timingsthenFirebug.console##(time(Js.string"fast_select_nodes"));leta_nodeList:Dom_html.elementDom.nodeListJs.t=root##(querySelectorAll(Js.string("a."^Eliom_runtime.RawXML.ce_call_service_class)))inleta_nodeList:Dom_html.anchorElementDom.nodeListJs.t=Js.Unsafe.coercea_nodeListinletform_nodeList:Dom_html.elementDom.nodeListJs.t=root##(querySelectorAll(Js.string("form."^Eliom_runtime.RawXML.ce_call_service_class)))inletform_nodeList:Dom_html.formElementDom.nodeListJs.t=Js.Unsafe.coerceform_nodeListinletprocess_node_nodeList=root##(querySelectorAll(Js.string("."^Eliom_runtime.RawXML.process_node_class)))inletclosure_nodeList=root##(querySelectorAll(Js.string("."^Eliom_runtime.RawXML.ce_registered_closure_class)))inletattrib_nodeList=root##(querySelectorAll(Js.string("."^Eliom_runtime.RawXML.ce_registered_attr_class)))inif!Eliom_config.debug_timingsthenFirebug.console##(timeEnd(Js.string"fast_select_nodes"));(a_nodeList,form_nodeList,process_node_nodeList,closure_nodeList,attrib_nodeList)letslow_has_classes(node:Dom_html.elementJs.t)=letclasses=(* IE<9: className is not set after change_page; getAttribute("class")
does not work for the initial document *)letstr=ifnode##.className=Js.string""thenJs.Opt.getnode##(getAttribute(Js.string"class"))(fun()->Js.string"")elsenode##.classNameinJs.str_arraystr##(split(Js.string" "))inletfound_call_service=reffalseinletfound_process_node=reffalseinletfound_closure=reffalseinletfound_attrib=reffalseinfori=0toclasses##.length-1dofound_call_service:=Js.array_getclassesi==Js.def(Js.stringEliom_runtime.RawXML.ce_call_service_class)||!found_call_service;found_process_node:=Js.array_getclassesi==Js.def(Js.stringEliom_runtime.RawXML.process_node_class)||!found_process_node;found_closure:=Js.array_getclassesi==Js.def(Js.stringEliom_runtime.RawXML.ce_registered_closure_class)||!found_closure;found_attrib:=Js.array_getclassesi==Js.def(Js.stringEliom_runtime.RawXML.ce_registered_attr_class)||!found_attribdone;!found_call_service,!found_process_node,!found_closure,!found_attribletslow_has_request_class(node:Dom_html.elementJs.t)=letclasses=Js.str_arraynode##.className##(split(Js.string" "))inletfound_request_node=reffalseinfori=0toclasses##.length-1dofound_request_node:=Js.array_getclassesi==Js.def(Js.stringEliom_runtime.RawXML.request_node_class)||!found_request_nodedone;!found_request_nodeletfast_has_classes(node:Dom_html.elementJs.t)=(Js.to_boolnode##.classList##(contains(Js.stringEliom_runtime.RawXML.ce_call_service_class)),Js.to_boolnode##.classList##(contains(Js.stringEliom_runtime.RawXML.process_node_class)),Js.to_boolnode##.classList##(contains(Js.stringEliom_runtime.RawXML.ce_registered_closure_class)),Js.to_boolnode##.classList##(contains(Js.stringEliom_runtime.RawXML.ce_registered_attr_class)))letfast_has_request_class(node:Dom_html.elementJs.t)=Js.to_boolnode##.classList##(contains(Js.stringEliom_runtime.RawXML.request_node_class))lethas_classes:Dom_html.elementJs.t->bool*bool*bool*bool=iftest_classList()thenfast_has_classeselseslow_has_classeslethas_request_class:Dom_html.elementJs.t->bool=iftest_classList()thenfast_has_request_classelseslow_has_request_classletslow_select_request_nodes(root:Dom_html.elementJs.t)=letnode_array=new%jsJs.array_emptyinletrectraverse(node:Dom.nodeJs.t)=matchnode##.nodeTypewith|Dom.ELEMENT->letnode=(Js.Unsafe.coercenode:Dom_html.elementJs.t)inifhas_request_classnodethenignorenode_array##(pushnode);iter_nodeListnode##.childNodestraverse|_->()intraverse(root:>Dom.nodeJs.t);(Js.Unsafe.coercenode_array:Dom_html.elementDom.nodeListJs.t)letslow_select_nodes(root:Dom_html.elementJs.t)=leta_array=new%jsJs.array_emptyinletform_array=new%jsJs.array_emptyinletnode_array=new%jsJs.array_emptyinletclosure_array=new%jsJs.array_emptyinletattrib_array=new%jsJs.array_emptyinletrectraverse(node:Dom.nodeJs.t)=matchnode##.nodeTypewith|Dom.ELEMENT->letnode=(Js.Unsafe.coercenode:Dom_html.elementJs.t)inletcall_service,process_node,closure,attrib=has_classesnodein(ifcall_servicethenmatchDom_html.taggednodewith|Dom_html.Ae->ignorea_array##(pushe)|Dom_html.Forme->ignoreform_array##(pushe)|_->Lwt_log.raise_error_f~section"%s element tagged as eliom link"(Js.to_stringnode##.tagName));ifprocess_nodethenignorenode_array##(pushnode);ifclosurethenignoreclosure_array##(pushnode);ifattribthenignoreattrib_array##(pushnode);iter_nodeListnode##.childNodestraverse|_->()intraverse(root:>Dom.nodeJs.t);((Js.Unsafe.coercea_array:Dom_html.anchorElementDom.nodeListJs.t),(Js.Unsafe.coerceform_array:Dom_html.formElementDom.nodeListJs.t),(Js.Unsafe.coercenode_array:Dom_html.elementDom.nodeListJs.t),(Js.Unsafe.coerceclosure_array:Dom_html.elementDom.nodeListJs.t),(Js.Unsafe.coerceattrib_array:Dom_html.elementDom.nodeListJs.t))letselect_nodes=iftest_querySelectorAll()thenfast_select_nodeselseslow_select_nodesletselect_request_nodes=iftest_querySelectorAll()thenfast_select_request_nodeselseslow_select_request_nodes(* createEvent for ie < 9 *)letcreateEvent_ieev_type=letevt:#Dom_html.eventJs.t=(Js.Unsafe.coerceDom_html.document)##createEventObjectin(Js.Unsafe.coerceevt)##._type:=(Js.string"on")##(concatev_type);evtletcreateEvent_normalev_type=letevt:#Dom_html.eventJs.t=(Js.Unsafe.coerceDom_html.document)##(createEvent(Js.string"HTMLEvents"))inlet()=(Js.Unsafe.coerceevt)##(initEventev_typefalsefalse)inevtletcreateEvent=iftest_createEvent()thencreateEvent_normalelsecreateEvent_ie(* DOM traversal *)classtype['element]get_tag=objectmethodgetElementsByTagName:Js.js_stringJs.t->'elementDom.nodeListJs.tJs.methend(* We can't use Dom_html.document##head: it is not defined in ff3.6... *)letget_head(page:'element#get_tagJs.t):'elementJs.t=Js.Opt.getpage##(getElementsByTagName(Js.string"head"))##(item0)(fun()->Lwt_log.raise_error~section"get_head")letget_body(page:'element#get_tagJs.t):'elementJs.t=Js.Opt.getpage##(getElementsByTagName(Js.string"body"))##(item0)(fun()->Lwt_log.raise_error~section"get_body")letiter_dom_array(f:'a->unit)(a:<length:<get:int;..>Js.gen_prop;item:int->'aJs.optJs.meth;..>Js.t)=letlength=a##.lengthinfori=0tolength-1doJs.Opt.itera##(itemi)fdoneletcopy_textt=Dom_html.document##(createTextNodet##.data)(* ie, ff3.6 and safari does not like setting innerHTML on html and
head nodes: we need to rebuild the HTML dom tree from the XML dom
tree received in the xhr *)(* BEGIN IE<9 HACK:
appendChild is broken in ie:
see
http://webbugtrack.blogspot.com/2009/01/bug-143-createtextnode-doesnt-work-on.html
http://webbugtrack.blogspot.com/2007/10/bug-142-appendchild-doesnt-work-on.html
This fix appending to script element.
TODO: it is also broken when appending tr to tbody, need to find a solution
*)letadd_childrens(elt:Dom_html.elementJs.t)(sons:Dom.nodeJs.tlist)=tryList.iter(Dom.appendChildelt)sonswithexn->((* this code is ie only, there are no reason for an appendChild
to fail normally *)letconcatl=letrecconcatacc=function|[]->acc|t::q->lettxt=matchDom.nodeTypetwith|Dom.Textt->t|_->Lwt_log.raise_error_f~section"add_childrens: not text node in tag %s"(Js.to_stringelt##.tagName)inconcatacc##(concattxt##.data)qinconcat(Js.string"")linmatchDom_html.taggedeltwith|Dom_html.Scriptelt->elt##.text:=concatsons|Dom_html.Styleelt->(* we need to append the style node to something. If we
don't do that the styleSheet field is not created if we.
And we can't do it by creating it with the ie specific
document.createStyleSheet: the styleSheet field is not
initialised and it can't be set either. *)letd=Dom_html.createHeadDom_html.documentinDom.appendChilddelt;(Js.Unsafe.coerceelt)##.styleSheet##.cssText:=concatsons|_->Lwt_log.raise_error~section~exn"add_childrens: can't appendChild")(* END IE HACK *)letcopy_element(e:Dom.elementJs.t)(registered_process_node:Js.js_stringJs.t->bool):Dom_html.elementJs.t=letrecaux(e:Dom.elementJs.t)=letcopy=Dom_html.document##(createElemente##.tagName)in(* IE<9: Copy className separately, it's not updated when displayed *)Js.Opt.iter(Dom_html.CoerceTo.elemente)(fune->copy##.className:=e##.className);letnode_id=Js.Opt.to_optione##(getAttribute(Js.stringEliom_runtime.RawXML.node_id_attrib))inmatchnode_idwith|Someidwhenregistered_process_nodeid->Js.Opt.itere##(getAttribute(Js.string"class"))(funclasses->copy##(setAttribute(Js.string"class")classes));copy##(setAttribute(Js.stringEliom_runtime.RawXML.node_id_attrib)id);Somecopy|_->letadd_attributea=Js.Opt.iter(Dom.CoerceTo.attra)(* we don't use copy##attributes##setNameditem:
in ie 9 it fail setting types of buttons... *)(funa->copy##(setAttributea##.namea##.value))initer_dom_arrayadd_attributee##.attributes;letchild_copies=List.map_filter(funchild->matchDom.nodeTypechildwith|Dom.Textt->Some(copy_textt:>Dom.nodeJs.t)|Dom.Elementchild->(auxchild:>Dom.nodeJs.toption)|_->None)(Dom.list_of_nodeListe##.childNodes)inadd_childrenscopychild_copies;Somecopyinmatchauxewith|None->Lwt_log.raise_error~section"copy_element"|Somee->elethtml_document(src:Dom.elementDom.documentJs.t)registered_process_node:Dom_html.elementJs.t=letcontent=src##.documentElementinmatchJs.Opt.to_option(Dom_html.CoerceTo.elementcontent)with|Somee->(tryDom_html.document##(adoptNode(e:>Dom.elementJs.t))withexn->(Lwt_log.ign_debug~section~exn"can't adopt node, import instead";tryDom_html.document##(importNode(e:>Dom.elementJs.t)Js._true)withexn->Lwt_log.ign_debug~section~exn"can't import node, copy instead";copy_elementcontentregistered_process_node))|None->Lwt_log.ign_debug~section"can't adopt node, document not parsed as html. copy instead";copy_elementcontentregistered_process_node(** CSS preloading. *)letspaces_re=Regexp.regexp" +"letis_stylesheete=(* FIX: should eventually use Dom_html.element *)Js.Opt.case(Dom_html.CoerceTo.link(Js.Unsafe.coercee))(fun_->false)(fune->List.exists(funs->s="stylesheet")(Regexp.splitspaces_re(Js.to_stringe##.rel))&&e##._type==Js.string"text/css")letbasedir_re=Regexp.regexp"^(([^/?]*/)*)([^/?]*)(\\?.*)?$"letbasedirpath=matchRegexp.string_matchbasedir_repath0with|None->"/"|Someres->(matchRegexp.matched_groupres1with|None->(matchRegexp.matched_groupres3withSome".."->"../"|_->"/")|Somedir->(matchRegexp.matched_groupres3with|Some".."->dir^"../"|_->dir))letfetch_linked_csse=letrecextractacc(e:Dom.nodeJs.t)=matchDom.nodeTypeewith|Dom.Elementewhenis_stylesheete->lete:Dom_html.linkElementJs.t=Js.Unsafe.coerceeinlethref=e##.hrefinifJs.to_boole##.disabled||e##.title##.length>0||href##.length=0thenaccelselethref=Js.to_stringhrefinletcss=Eliom_request.http_gethref[]Eliom_request.string_resultinacc@[e,(e##.media,href,css>|=snd)]|Dom.Elemente->letc=e##.childNodesinletacc=refaccinfori=0toc##.length-1doacc:=extract!acc(Js.Opt.getc##(itemi)(fun_->assertfalse))done;!acc|_->accinextract[](e:>Dom.nodeJs.t)leturl_content_raw="([^'\\\"]([^\\\\\\)]|\\\\.)*)"letdbl_quoted_url_raw="\"(([^\\\\\"]|\\\\.)*)\""letquoted_url_raw="'(([^\\\\']|\\\\.)*)'"leturl_re=Regexp.regexp(Printf.sprintf"url\\s*\\(\\s*(%s|%s|%s)\\s*\\)\\s*"dbl_quoted_url_rawquoted_url_rawurl_content_raw)letraw_url_re=Regexp.regexp(Printf.sprintf"\\s*(%s|%s)\\s*"dbl_quoted_url_rawquoted_url_raw)letabsolute_re=Regexp.regexp"\\s*(https?:\\/\\/|data:|file:|\\/)"letabsolute_re2=Regexp.regexp"['\\\"]\\s*((https?:\\/\\/|data:|file:|\\/).*)['\\\"]$"exceptionIncorrect_urlletparse_absolute~prefixhref=matchRegexp.searchabsolute_rehref0with|Some(i,_)wheni=0->(* absolute URL -> do not rewrite *)href|_->(matchRegexp.searchabsolute_re2href0with|Some(i,res)wheni=0->(matchRegexp.matched_groupres1with|Somehref->(* absolute URL -> do not rewrite *)href|None->raiseIncorrect_url)|_->prefix^href)letparse_url~prefixcsspos=matchRegexp.searchurl_recssposwith|Some(i,res)wheni=pos->((i+String.length(Regexp.matched_stringres),matchRegexp.matched_groupres2with|Somehref->parse_absolute~prefixhref|None->(matchRegexp.matched_groupres3with|Somehref->parse_absolute~prefixhref|None->(matchRegexp.matched_groupres4with|Somehref->parse_absolute~prefixhref|None->raiseIncorrect_url))))|_->(matchRegexp.searchraw_url_recssposwith|Some(i,res)wheni=pos->((i+String.length(Regexp.matched_stringres),matchRegexp.matched_groupres1with|Somehref->parse_absolute~prefixhref|None->raiseIncorrect_url))|_->raiseIncorrect_url)letparse_mediacsspos=leti=tryString.index_fromcsspos';'withNot_found->String.lengthcssini+1,String.subcsspos(i-pos)(* Look for relative URL only... *)leturl_re=Regexp.regexp"url\\s*\\(\\s*(?!('|\")?(https?:\\/\\/|data:|file:|\\/))"letrewrite_css_url~prefixcsspos=letlen=String.lengthcss-posinletbuf=Buffer.create(len+(len/2))inletrecrewritepos=ifpos<String.lengthcssthenmatchRegexp.searchurl_recssposwith|None->Buffer.add_substringbufcsspos(String.lengthcss-pos)|Some(i,_res)->(Buffer.add_substringbufcsspos(i-pos);tryleti,href=parse_url~prefixcssiinBuffer.add_stringbuf"url('";Buffer.add_stringbufhref;Buffer.add_stringbuf"')";rewriteiwithIncorrect_url->Buffer.add_substringbufcssi(String.lengthcss-i))inrewritepos;Buffer.contentsbufletimport_re=Regexp.regexp"@import\\s*"letrecrewrite_css~max(media,href,css)=try%lwtcss>>=function|None->Lwt.return_nil|Somecss->if!Eliom_config.debug_timingsthenFirebug.console##(time(Js.string("rewrite_CSS: "^href)));let%lwtimports,css=rewrite_css_import~max~prefix:(basedirhref)~mediacss0inif!Eliom_config.debug_timingsthenFirebug.console##(timeEnd(Js.string("rewrite_CSS: "^href)));Lwt.return(imports@[media,css])with_->Lwt.return[media,Printf.sprintf"@import url(%s);"href]andrewrite_css_import?(charset="")~max~prefix~mediacsspos=matchRegexp.searchimport_recssposwith|None->(* No @import anymore, rewrite url. *)Lwt.return([],rewrite_css_url~prefixcsspos)|Some(i,res)->((* Found @import rule, try to preload. *)letinit=String.subcsspos(i-pos)inletcharset=ifpos=0theninitelsecharsetintryleti=i+String.length(Regexp.matched_stringres)inleti,href=parse_url~prefixcssiinleti,media'=parse_mediacssiinlet%lwtimport=ifmax=0then(* Maximum imbrication of @import reached, rewrite url. *)Lwt.return[media,Printf.sprintf"@import url('%s') %s;\n"hrefmedia']elseifmedia##.length>0&&String.lengthmedia'>0then(* TODO combine media if possible...
in the mean time keep explicit import. *)Lwt.return[media,Printf.sprintf"@import url('%s') %s;\n"hrefmedia']elseletmedia=ifmedia##.length>0thenmediaelseJs.stringmedia'inletcss=Eliom_request.http_gethref[]Eliom_request.string_resultinrewrite_css~max:(max-1)(media,href,css>|=snd)andimports,css=rewrite_css_import~charset~max~prefix~mediacssiinLwt.return(import@imports,css)with|Incorrect_url->Lwt.return([],rewrite_css_url~prefixcsspos)|exn->Lwt_log.ign_info~section~exn"Error while importing css";Lwt.return([],rewrite_css_url~prefixcsspos))letmax_preload_depth=ref4letbuild_style(e,css)=let%lwtcss=rewrite_css~max:!max_preload_depthcssin(* lwt css = *)Lwt_list.map_p(fun(media,css)->letstyle=Dom_html.createStyleDom_html.documentinstyle##._type:=Js.string"text/css";style##.media:=media;(* IE8: Assigning to style##innerHTML results in
"Unknown runtime error" *)letstyleSheet=Js.Unsafe.(getstyle(Js.string"styleSheet"))inifJs.Optdef.teststyleSheetthenJs.Unsafe.(setstyleSheet(Js.string"cssText")(Js.stringcss))elsestyle##.innerHTML:=Js.stringcss;Lwt.return(e,(style:>Dom.nodeJs.t)))css(* IE8 doesn't allow appendChild on noscript-elements *)(* (\* Noscript is used to group style. It's ignored by the parser when *)(* scripting is enabled, but does not seems to be ignore when *)(* inserted as a DOM element. *\) *)(* let node = Dom_html.createNoscript Dom_html.document in *)(* List.iteri (fun i x -> debug "HOC 3.%i" i; Dom.appendChild node x) css; *)(* Lwt.return (e, node )*)letpreload_css(doc:Dom_html.elementJs.t)=if!Eliom_config.debug_timingsthenFirebug.console##(time(Js.string"preload_css (fetch+rewrite)"));let%lwtcss=Lwt_list.map_pbuild_style(fetch_linked_css(get_headdoc))inletcss=List.concatcssinList.iter(fun(e,css)->tryDom.replaceChild(get_headdoc)cssewith_->(* Node was a unique node that has been removed...
in a perfect settings we won't have parsed it... *)Lwt_log.ign_info~section"Unique CSS skipped...")css;if!Eliom_config.debug_timingsthenFirebug.console##(timeEnd(Js.string"preload_css (fetch+rewrite)"));Lwt.return_unit(** Window scrolling *)(* Correct scrolling information in Chromium are found
Dom_html.document##body while on Firefox they are found on
Dom_html.document##documentElement. *)typeposition={html_top:int;html_left:int;body_top:int;body_left:int}lettop_position={html_top=0;html_left=0;body_top=0;body_left=0}letcreateDocumentScroll()={html_top=Dom_html.document##.documentElement##.scrollTop;html_left=Dom_html.document##.documentElement##.scrollLeft;body_top=Dom_html.document##.body##.scrollTop;body_left=Dom_html.document##.body##.scrollLeft}(* With firefox, the scroll position is restored before to fire the
popstate event. We maintain our own position. *)letcurrent_position=reftop_positionlet_=(* HACK: Remove this when js_of_ocaml 1.1.2 or greater is released... *)(* window##onscroll <- *)ignore(Dom.addEventListenerDom_html.document(Dom.Event.make"scroll")(Dom_html.handler(fun_event->current_position:=createDocumentScroll();Js._false))Js._true:Dom_html.event_listener_id)letgetDocumentScroll()=!current_positionletsetDocumentScrollpos=Dom_html.document##.documentElement##.scrollTop:=pos.html_top;Dom_html.document##.documentElement##.scrollLeft:=pos.html_left;Dom_html.document##.body##.scrollTop:=pos.body_top;Dom_html.document##.body##.scrollLeft:=pos.body_left;current_position:=pos(* UGLY HACK for Opera bug: Opera seem does not always take into
account the content of the base element. If we touch it like that,
it remember its presence... *)lettouch_base()=Js.Opt.iter(Js.Opt.bindDom_html.document##(getElementById(Js.stringEliom_common_base.base_elt_id))Dom_html.CoerceTo.base)(fune->lethref=e##.hrefine##.href:=href)(* BEGIN FORMDATA HACK: This is only needed if FormData is not available in the browser.
When it will be commonly available, remove all sections marked by "FORMDATA HACK" !
Notice: this hack is used to circumvent a limitation in FF4 implementation of formdata:
if the user click on a button in a form, formdatas created in the onsubmit callback normally contains the value of the button. ( it is the behaviour of chromium )
in FF4, it is not the case: we must do this hack to find which button was clicked.
NOTICE: this may not be corrected the way we want:
see https://bugzilla.mozilla.org/show_bug.cgi?id=647231
html5 will explicitly specify that chromium behaviour is wrong...
This is implemented in:
* this file -> here and called in load_eliom_data
* Eliom_request: in send_post_form
* in js_of_ocaml, module Form: the code to emulate FormData *)letonclick_on_body_handlerevent=(matchDom_html.tagged(Dom_html.eventTargetevent)with|Dom_html.Buttonbutton->Js.Unsafe.global##.eliomLastButton:=Somebutton|Dom_html.Inputinputwheninput##._type=Js.string"submit"->Js.Unsafe.global##.eliomLastButton:=Someinput|_->Js.Unsafe.global##.eliomLastButton:=None);Js._trueletadd_formdata_hack_onclick_handler()=ignore(Dom_html.addEventListenerDom_html.window##.document##.bodyDom_html.Event.click(Dom_html.handleronclick_on_body_handler)Js._true:Dom_html.event_listener_id)(* END FORMDATA HACK *)(** onhashchange *)lethashchange=Dom.Event.make"hashchange"letonhashchangef=iftest_onhashchange()thenignore(Dom.addEventListenerDom_html.windowhashchange(Dom_html.handler(fun_->fDom_html.window##.location##.hash;Js._false))Js._true:Dom_html.event_listener_id)elseletlast_fragment=refDom_html.window##.location##.hashinletcheck()=if!last_fragment!=Dom_html.window##.location##.hashthen(last_fragment:=Dom_html.window##.location##.hash;fDom_html.window##.location##.hash)inignoreDom_html.window##(setInterval(Js.wrap_callbackcheck)(0.2*.1000.))